基于 Webpack 3 的 Vue.js 工程項(xiàng)目腳手架
基于 Webpack 3 的 Vue.js 工程項(xiàng)目腳手架從屬于筆者的 Web 前端入門(mén)與工程實(shí)踐,是筆者基于兄弟項(xiàng)目 React 腳手架改造而來(lái),二者在 Webpack 配置層面差異不大。更多關(guān)于 Vue.js 或者前端開(kāi)發(fā)相關(guān)的資料鏈接可以參考Vue.js 學(xué)習(xí)與實(shí)踐資料索引,Vue.js 與前端工程化實(shí)踐系列文章以及 Webpack 學(xué)習(xí)與資料索引,對(duì)于其中淺薄的工程化的思考可以參考 2016-我的前端之路:工具化與工程化。
基于 Webpack 3 的 Vue.js 工程項(xiàng)目腳手架
create-webpack-app是筆者對(duì)于日常工作中的基于 React/Vue.js 技術(shù)棧與實(shí)踐的沉淀,dev-config/* 與 package.json 構(gòu)成了基礎(chǔ)的腳手架,支持最新的開(kāi)發(fā)流程與默認(rèn)的生產(chǎn)環(huán)境優(yōu)化;模板項(xiàng)目包含特性如下:
- 技術(shù)棧支持:使用 ES6/ES7 語(yǔ)法、允許使用 CSS Modules、SCSS、Less 并且使用 PostCSS 進(jìn)行自動(dòng) Polyfill、支持使用 styled-component 進(jìn)行 CSS-in-JS 樣式控制、使用 Flow 作為靜態(tài)類(lèi)型檢測(cè)工具、使用 Jest 作為默認(rèn)的測(cè)試框架
 - 開(kāi)發(fā)環(huán)境:使用 WebpackDevServer 部署開(kāi)發(fā)服務(wù)器、使用 React Hot Loader 進(jìn)行組件熱加載、使用 Babel 進(jìn)行代碼轉(zhuǎn)換、使用 ESLint 進(jìn)行代碼檢測(cè)、使用 DllPlugin 作為開(kāi)發(fā)環(huán)境下公共代碼提取工具以?xún)?yōu)化編譯速度
 - 生產(chǎn)環(huán)境:使用 CommonChunksPlugin 作為生產(chǎn)環(huán)境下公共代碼提取工具、使用 Prepack & prepack-webpack-plugin 進(jìn)行代碼優(yōu)化、使用 offline-plugin 添加簡(jiǎn)單的 PWA 特性增強(qiáng)
 - 部署方式:支持獨(dú)立部署(Hash 方式切換路由)、支持服務(wù)端部署、支持服務(wù)端渲染
 
本部分即是針對(duì) Vue.js 項(xiàng)目的腳手架,我們可以直接拷貝該項(xiàng)目來(lái)展示部分開(kāi)發(fā)模式或者作為模板項(xiàng)目使用:
- # 下載本項(xiàng)目
 - git clone https://github.com/wxyyxc1992/create-webpack-app
 - # 可以使用 yarn install & npm start 直接運(yùn)行本項(xiàng)目
 - # 僅保留 dev-config、package.json、src/client.js、src/ssr_server.js
 - mkdir /path/to/your/project
 - # 拷貝必須的啟動(dòng)文件
 - cp -r vue/dev-config/ /path/to/your/project
 - cp vue/package.json /path/to/your/project
 - cp vue/src/client.js /path/to/your/project/src/
 - # 安裝運(yùn)行依賴(lài)
 - cd /path/to/your/project
 - yarn install / npm install
 - # 有時(shí)候需要安裝 better-npm-run
 - npm install better-npm-run -g
 - # 啟動(dòng)項(xiàng)目
 - npm start
 - # 編譯為純客戶(hù)端部署模式,即單個(gè) HTML 頁(yè)面
 - npm run build
 - # 進(jìn)行依賴(lài)升級(jí)檢查
 - npm run update
 
- 開(kāi)發(fā)環(huán)境下的操作面板:
 
- 編譯之后的包體分析:
 
本模板相較于官方的 webpack-simple 功能稍微復(fù)雜,并且引入了完整的 Flow、Jest 等技術(shù)棧配置,同時(shí)優(yōu)化了 Webpack 的構(gòu)建性能。
!Important! 項(xiàng)目尚未支持 SSR
基礎(chǔ)配置
create-webpack-app 默認(rèn)的應(yīng)用配置位于 dev-config/apps.config.js 文件中,該文件也是 dev-config/ 文件夾下唯一與應(yīng)用業(yè)務(wù)相關(guān)的文件;該文件定義了不同應(yīng)用中的需要配置的應(yīng)用相關(guān)信息。create-webpack-app 定位為單項(xiàng)目多應(yīng)用的模板,因此我們可以在apps 鍵下配置項(xiàng)目設(shè)計(jì)的應(yīng)用入口;在打包時(shí)會(huì)自動(dòng)將多個(gè)應(yīng)用并行編譯并且提取出所有公共的代碼。每個(gè)應(yīng)用需要提供唯一編號(hào)、入口文件地址、模板頁(yè)面、是否編譯等信息;接下來(lái) devServer 則是定義了當(dāng)前正在開(kāi)發(fā)的應(yīng)用入口,ssrServer 定義了打包時(shí)需要使用的渲染服務(wù)器入口,其會(huì)在執(zhí)行 npm run build:ssr 時(shí)調(diào)用,proxy 與 api 則定義了后端服務(wù)器信息,開(kāi)發(fā)者可以根據(jù)業(yè)務(wù)需求自行使用。典型的 apps.config.js 文件配置如下:
- module.exports = {
 - //基本的應(yīng)用配置信息
 - apps: [
 - //HelloWorld
 - {
 - id: "pwa",
 - src: "./pwa/client.js",
 - indexPage: defaultIndexPage,
 - compiled: true
 - }
 - ],
 - //開(kāi)發(fā)入口配置
 - devServer: {
 - appEntrySrc: "./pwa/client.js", //當(dāng)前待調(diào)試的APP的入口文件
 - port: 3000 //監(jiān)聽(tīng)的Server端口
 - },
 - //用于服務(wù)端渲染的Server路徑
 - ssrServer: {
 - serverEntrySrc: "./pwa/ssr_server.js"
 - },
 - //依賴(lài)項(xiàng)配置
 - proxy: {
 - //后端服務(wù)器地址 http://your.backend/
 - "/api/*": "http://localhost:3001"
 - },
 - //后端 api 配置,這樣配置可以避免將測(cè)試服務(wù)器端口暴露出去
 - api: {
 - dev: {},
 - prod: {}
 - }
 - };
 
應(yīng)用入口文件則遵循官方的單文件組件范式:
- import Vue from 'vue';
 - import App from './application/App.vue';
 - new Vue({
 - el: '#root',
 - render: h => h(App)
 - });
 
腳本編譯與熱加載
在 dev-config/webpack/loaders.js 文件中定義了模板所需要的加載器,默認(rèn)支持 js、jsx、vue、ts、tsx、css、scss、less、json 以及各種資源文件等常見(jiàn)格式。當(dāng)我們執(zhí)行 npm start 命令時(shí),會(huì)自動(dòng)啟動(dòng)dev-config/server/devServer.js 文件中定義的 Webpack 開(kāi)發(fā)服務(wù)器,該服務(wù)器會(huì)使用 dev-config/webpack.config.js 文件進(jìn)行配置項(xiàng)生成。值得一提的是,WebpackDevServer 中的 contentBase 設(shè)置為了 path.join(__dirname, "../../public"),也就是將 /public 目錄作為開(kāi)發(fā)服務(wù)器的默認(rèn)根目錄。熱加載配置包括以下步驟:
- 開(kāi)發(fā)時(shí)應(yīng)用入口設(shè)置:
 
- entry = [
 - `webpack-dev-server/client?http://0.0.0.0:${appsConfig.devServer.port}`,
 - "webpack/hot/only-dev-server",
 - require("./apps.config.js").devServer.appEntrySrc
 - ];
 
- Babel 配置,默認(rèn)的 Babel 文件位于 dev-config/tool/.babelrc:
 
- ...
 - "plugins": [
 - ...
 
樣式處理
create-webpack-app 支持 SCSS、CSS Modules 以及 styled-components 這三種樣式定義方式。
Webpack 性能優(yōu)化
公共代碼分割
create-webpack-app 使用了 CommonsChunkPlugin 進(jìn)行代碼分割,默認(rèn)在 dev-config/webpack/plugins.js 文件中定義了對(duì)于 node_modules 中依賴(lài)文件的自動(dòng)抽?。?/p>
- new webpack.optimize.CommonsChunkPlugin({
 - name: "vendor",
 - filename: "vendor.bundle.js",
 - minChunks: ({ resource }) =>
 - resource &&
 - resource.indexOf("node_modules") >= 0 &&
 - resource.match(/\.(js|less|scss)$/)
 - })
 
該插件會(huì)自動(dòng)生成 vendor.bundle.js 文件,我們需要在應(yīng)用入口文件之前引用它;開(kāi)發(fā)者也可以自定義 CommonsChunkPlugin 插件以自定義需要提取的公共代碼。
構(gòu)建性能優(yōu)化
隨著項(xiàng)目復(fù)雜度與體量的增加,我們發(fā)現(xiàn)初始化編譯與增量編譯的速度都有所下降,為了提升構(gòu)建性能首先我們要做的就是保持 Webpack 版本的更新速度;此外,create-webpack-app 還默認(rèn)啟動(dòng)了 DllPlugin 在開(kāi)發(fā)狀態(tài)下將所有的依賴(lài)提取為 dll 文件以提高增量編譯的速度。因?yàn)榭紤]到靈活性,即隨時(shí)有可能增減依賴(lài)的情況,create-webpack-app 目前設(shè)置的是每次使用 npm start 的時(shí)候都會(huì)重新生成 dll 文件;如果是已經(jīng)穩(wěn)定的項(xiàng)目可以考慮僅生成一次依賴(lài)。
- const path = require("path");
 - const pkg = require("../package.json");
 - const webpack = require("webpack");
 - let dllConfig = {
 - name: "vendor",
 - entry: Object.keys(pkg.dependencies),
 - output: {
 - path: path.resolve(__dirname, "../public/dll"),
 - filename: "vendor.bundle.js",
 - library: "vendor_[hash]"
 - },
 - plugins: [
 - new webpack.DllPlugin({
 - name: "vendor_[hash]",
 - path: path.resolve(__dirname, "../public/dll/manifest.json")
 - })
 - ]
 - };
 - module.exports = dllConfig;
 - // 在 public/index.html 文件中需要引入該依賴(lài)
 - // index.html
 - <script src="dll/vendor.bundle.js"></script>
 
代碼編譯優(yōu)化
create-webpack-app 中也內(nèi)置了其他的編譯之后的代碼性能優(yōu)化插件,首先是利用 Webpack 3 的 Scope Hositing 特性來(lái)優(yōu)化生成的模塊;這一點(diǎn)需要使用 ModuleConcatenationPlugin 插件。此外,還使用了 PrepackWebpackPlugin 對(duì)于打包生成的文件進(jìn)行過(guò)濾與重構(gòu);不過(guò)需要注意的是 PrepackWebpackPlugin 會(huì)較大地降低編譯速度,因此也是可以根據(jù)實(shí)際的項(xiàng)目情況選用。
- // 使用 Scope Hositing 特性
 - new webpack.optimize.ModuleConcatenationPlugin(),
 - // 使用 Prepack 優(yōu)化包體大小
 - // 暫時(shí)存在 Bug,等待修復(fù)
 - // 使用前 21 - 425
 - // 使用后 21 - 433
 - new PrepackWebpackPlugin({
 - mathRandomSeed: "0"
 - }),
 
PWA
create-webpack-app 中只是簡(jiǎn)單地使用了 Offline Plugin,其配置如下:
- // webpack.config.js example
 - var OfflinePlugin = require('offline-plugin');
 - module.exports = {
 - // ...
 - plugins: [
 - // ... other plugins
 - // it's always better if OfflinePlugin is the last plugin added
 - new OfflinePlugin()
 - ]
 - // ...
 - }
 - // render.js
 - require('offline-plugin/runtime').install();
 
觀察網(wǎng)絡(luò)面板中的資源請(qǐng)求情況,我們可以看到腳本等已經(jīng)被緩存在了本地:
設(shè)計(jì)模式
JavaScript 優(yōu)先
本小節(jié)僅代表本人個(gè)人意見(jiàn),請(qǐng)多多指點(diǎn)
官方文檔中介紹的單文件組件(Single File Component)包含了 template、script、style 這三個(gè)部分,筆者感覺(jué)算是典型的前端項(xiàng)目三要素。不過(guò)筆者習(xí)慣的開(kāi)發(fā)模式是以 JavaScript 為中心,即 JavaScript 文件單獨(dú)可測(cè)試,而不是和樣式以及標(biāo)簽混合在一起(JSX 本質(zhì)上也是 JavaScript)。因此在該模板中,筆者是將標(biāo)簽、樣式與腳本分到了三個(gè)文件中:
- // App.js
 - // @flow
 - export default {
 - name: "app",
 - data() {
 - return {
 - msg: "Vue.js & Webpack App Boilerplate by 王下邀月熊"
 - };
 - }
 - };
 - // App.scss
 - #app {
 - font-family: 'Avenir', Helvetica, Arial, sans-serif;
 - -webkit-font-smoothing: antialiased;
 - -moz-osx-font-smoothing: grayscale;
 - text-align: center;
 - color: #2c3e50;
 - margin-top: 60px;
 - }
 - h1, h2 {
 - font-weight: normal;
 - }
 - ul {
 - list-style-type: none;
 - padding: 0;
 - }
 - li {
 - display: inline-block;
 - margin: 0 10px;
 - }
 - a {
 - color: #42b983;
 - }
 - // App.vue
 - <template>
 - <div id="app">
 - <img src="../../public/assets/logo.png">
 - <h1>{{ msg }}</h1>
 - <h2>Essential Links</h2>
 - <ul>
 - <li><a href="https://vuejs.org" target="_blank">Core Docs</a></li>
 - <li><a href="https://forum.vuejs.org" target="_blank">Forum</a></li>
 - <li><a href="https://gitter.im/vuejs/vue" target="_blank">Gitter Chat</a></li>
 - <li><a href="https://twitter.com/vuejs" target="_blank">Twitter</a></li>
 - </ul>
 - <h2>Ecosystem</h2>
 - <ul>
 - <li><a href="http://router.vuejs.org/" target="_blank">vue-router</a></li>
 - <li><a href="http://vuex.vuejs.org/" target="_blank">vuex</a></li>
 - <li><a href="http://vue-loader.vuejs.org/" target="_blank">vue-loader</a></li>
 - <li><a href="https://github.com/vuejs/awesome-vue" target="_blank">awesome-vue</a></li>
 - </ul>
 - </div>
 - </template>
 - <script>
 - import App from './App.js';
 - export default App;
 - </script>
 - <style lang="scss">
 - @import "App";
 - </style>
 
筆者個(gè)人感覺(jué)這種模式更符合單一職責(zé)原則,對(duì)于復(fù)雜的組件能夠提高代碼可讀性;同時(shí)將 JavaScript 代碼獨(dú)立出來(lái)也能更加方便地進(jìn)行單元測(cè)試與類(lèi)型檢測(cè)等操作。
代碼風(fēng)格
詳細(xì)的 JavaScript 編程樣式指南已經(jīng)遷移到了 Web 項(xiàng)目開(kāi)發(fā)風(fēng)格指南與 JavaScript 編程樣式指南,涵蓋了基本原則闡述、代碼風(fēng)格、代碼格式化與語(yǔ)法檢測(cè)、項(xiàng)目架構(gòu)等幾個(gè)部分。不過(guò)本部分建議是類(lèi)似于 Create React APP 配置提交時(shí)自動(dòng)進(jìn)行格式化,首先需要安裝如下依賴(lài):
- npm install --save husky lint-staged prettier
 - // or
 - yarn add husky lint-staged prettier
 
然后在 package.json 中添加 Hook:
- "scripts": {
 - "precommit": "lint-staged",
 - ...
 
同時(shí)添加 lint-staged 配置:
- "dependencies": {
 - // ...
 - },
 - + "lint-staged": {
 - + "{src,stories}/**/*.{js,jsx,json,css}": [
 - + "prettier --single-quote --write",
 - + "git add"
 - + ]
 - + },
 - "scripts": {
 
這樣當(dāng)我們提交代碼時(shí)就會(huì)自動(dòng)使用 Prettier 優(yōu)化代碼,不過(guò)需要注意的是這種配置僅作用于根目錄下;如果某個(gè)倉(cāng)庫(kù)中包含了多個(gè)應(yīng)用配置,那么我們還需要在根目錄下單獨(dú)配置腳本。我們也可以使用 ./node_modules/.bin/prettier --single-quote --write "src/**/*.{js,jsx}" 來(lái)手動(dòng)進(jìn)行項(xiàng)目文件的格式化。另外因?yàn)槟0屙?xiàng)目時(shí)放置在了子文件下,如果使用者希望使用該特性需要手動(dòng)地在 package.json 中添加 "precommit": "lint-staged" 這個(gè)配置。


















 
 
 









 
 
 
 