引子
一年前剛開始搞Cesium的時候還是使用的require.js進行模塊封裝,r.js進行打包,后面又用了gulp進行打包,但總感覺是不夠智能。require.js自然不用說了,It's too old,gulp配置也太麻煩,也是這之后才玩的webpack,后知后覺的,原來還有這么智能的打包工具啊。用它來打包Cesium項目挺香,雖然你們都喜歡Vue+Cesium的組合,但我還是偏愛ES6+webpack,原生質感讓我流連忘返。
預期效果
准備工作
今天問了下群友有沒有玩webpack+Cesium的,結果是沒又幾個人,基本都是Vue+Cesium,至於打包的工具嘛,好像現在又流行rullup。反正我是挺懵逼的,現在前端技術變化這么快嗎,webpack我才剛玩溜,一度懷疑這篇文章還有沒有繼續寫下去的必要了。后來想想還是有始有終吧,既然開了頭就要完成它,況且我覺得webpack還能再戰幾年啊,我還是堅持用它吧,畢竟熟悉嘛。閑話不多說了,開始准備工作。
新建項目
因為我打算從零開始搭建基於webpack的Cesium開源平台,本篇做為平台第一篇,所以要從新建項目開始。關於IDE我是用IDEA,大家先不要開噴為啥你做前端的不用VSCODE,因為我是做后台出身的,而且后面也有可能要上后台的,所以一套IDEA搞定前后兩端得了。
1、IDEA中點擊新建,選擇Javascript類型項目,輸入項目名稱,選擇創建好項目。我的項目名字叫simple-cesium,意為淺顯易懂、簡單易用的Cesium,符合深入淺出的理念。
2、創建一個src文件夾來存放源碼,創建一個css文件夾來存放樣式表,創建一個public文件夾來存放靜態文件。
3、新建一個index.js文件放到src文件夾中,內容:
console.log("Hello World!");
新建一個index.html文件,標題為simple-cesium,放到public文件夾下,內容:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="utf-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no"> 7 <title><%= htmlWebpackPlugin.options.title %></title> 8 </head> 9 <body> 10 <noscript> 11 <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript 12 enabled. Please enable it to continue.</strong> 13 </noscript> 14 <div id="cesiumContainer" class="cesiumContainer"> 15 <div id="cesiumSlider" class="cesiumSlider"></div> 16 <div id="creditContainer" class="creditContainer"></div> 17 </div> 18 <!-- built files will be auto injected --> 19 </body> 20 </html>
新建一個main.css文件,放到css文件夾中,內容:
1 @charset "utf-8"; 2 html { height: 100%; overflow: hidden;} 3 body { background: #000; color: #eee; font-family: sans-serif; font-size: 9pt; padding: 0; margin: 0; width: 100%; height: 100%; overflow: hidden;} 4 5 .cesiumContainer { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; } 6 .cesiumSlider { display: none; position: absolute; left: 50%; top: 0; background-color: rgba(210, 255, 255, .5); width: 4px; height: 100%; z-index: 1; } 7 .cesiumSlider:hover { cursor: ew-resize; } 8 .creditContainer { display: none; }
4、創建package.json文件,后面的模塊依賴會自動寫入到這個文件中。 命令行輸入:
npm init
填入相關信息,生成package配置如下:
1 { 2 "name": "simple-cesium", 3 "version": "0.0.1", 4 "dependencies": {}, 5 "devDependencies": {}, 6 "description": "Make the Cesium simple!", 7 "main": "index.js", 8 "scripts": { 9 "test": "echo \"Error: no test specified\" && exit 1" 10 }, 11 "keywords": [ 12 "Simple", 13 "Cesium" 14 ], 15 "author": "Helsing", 16 "license": "Apache-2.0" 17 }
5、新建webpack.config.js文件,內容:
1 const config = {}; 2 module.exports = config;
這樣,一個空項目就建好了。目錄結構如下:
|- simple-cesium
|- .idea
|- css
main.css
|- node_modules
|- public
favicon.ico
index.html
|- src
index.js
index.html
package.json
webpack.config.js
安裝webpack
在安裝webpack之前需要先安裝node.js,至於安裝方法不贅述了,請自行查閱資料。我的node和npm版本分別為10.16.1和6.9.0,這個是之前裝好的,因為我覺得它們的版本對本項目影響不大,所以沒更新到最新版本。
Terminal中執行命令:
npm -i webpack -D
這樣就將webpack安裝到項目中了,如果要全局安裝請使用-g參數。安裝后,package.json文件發生了變化:
1 { 2 "name": "simple-cesium", 3 "version": "0.0.1", 4 "dependencies": {}, 5 "devDependencies": { 6 "webpack": "^5.9.0" 7 } 8 ... 9 }
我們看到webpack已經被安裝到開發依賴中了,版本是5.9.0,這里要說明的是,webpack最近的大版本升級4.0到5.0變化是巨大的,而且5.0現在還存在着很多BUG以及插件的適配問題,目前的生產環境暫時不推薦升級,我用的最新版本,踩坑是難免的。
webpack在4.0以后都要安裝webpack-cli,所以繼續執行命令:
npm i webpack-cli -D
上述都是使用開發環境依賴安裝,如須全局安裝選擇參數-g即可。現在去看看node_modules目錄的下面,“全家桶”已裝滿,說明webpack算安裝完成了。
配置webpack
我們都知道webpack之所以強大,和它的插件生態密不可分的,所以安裝完webpack只是第一步,后面還要上很多的插件。
1、webpack-dev-server,我認為這最重要的插件,開發調試之利器也。
首先,安裝webpack-dev-server,執行命令:
npm i webpack-dev-server -D
然后,配置一下package.json文件,在里面添加調試腳本:
1 "scripts": { 2 "test": "echo \"Error: no test specified\" && exit 1", 3 "build": "cross-env NODE_ENV=production node_modules/.bin/webpack --progress --config webpack.config.js", 4 "start": "cross-env NODE_ENV=development node_modules/.bin/webpack-dev-server --progress --config webpack.config.js --open chrome --hot" 5 }
生產模式下使用build,開發模式下使用start進行調試,open參數設置打開chrome瀏覽器,hot參數設置熱更新。另外,上面出現了cross-env,它是一個可以運行跨平台設置和使用環境變量的插件,可以讓你在Windows系統下使用process.env.NODE_ENV。
需要事先安裝cross-env,執行命令:
npm i cross-env -D
最后,來配置下webapck.config.json文件:
1 const config = { 2 devServer: { 3 hot: true, // 是否啟用熱更新。 4 contentBase: path.join(__dirname, "dist"), // 告訴服務器從哪里提供內容。只有在你想要提供靜態文件時才需要。 5 // openPage: "index.html", // 指定打開的頁面。指定路徑會改變URL地址。 6 compress: true, // 是否啟用gzip壓縮。 7 port: 9999, // 端口號。 8 lazy: false // 當啟用lazy時,dev-server只有在請求時才編譯包(bundle)。這意味着webpack不會監視任何文件改動。我們稱之為“惰性模式”。 9 }, // 開發調試工具 10 }
上述步驟完成后,啟動start腳本調試項目,結果意料之中的拋出錯誤:
error:Cannot find module 'webpack-cli/bin/config-yargs'
上面說過了要踩坑的,這個算第一個吧。解決方法是改寫package.json中的start腳本:
"start": "cross-env NODE_ENV=development node_modules/.bin/webpack serve --progress --config webpack.config.js --open chrome --hot"
可以看出,並不是webpack5.0不兼容webpack-dev-server了,而是調用方式發生了變化,現在是使用webpack serve的方式啟動調試。
安裝Cesium
常規的Cesium開發是直接下載編譯好的庫,然后使用標簽或require方式進行全局引用,這種方式簡單,不需要進行安裝。但是這種方法沒有辦法將Cesium以模塊方式重新打包,而我們這篇文章的目的就是在講webpack與Cesium的結合,所以我們還要來安裝一下Cesium,執行命令:
npm i cesium -S
這里我們選擇的是生產環境依賴安裝,另外npm模塊安裝是區分大小寫的,cesium首字母為小寫,否則就是404了。
配置Cesium
在配置Cesium之前,我們先來完善一下webpack的基本配置:
1 const config = { 2 mode: nodeEnv, // 編譯模式:"production" | "development" | "none"。 3 context: __dirname, // 基礎目錄 4 entry: { 5 "simple-cesium": "./src/index.js" 6 }, // 入口:string | object | array。這里應用程序開始執行,webpack開始打包。 7 output: { 8 filename: "[name].js", // 輸出文件名:"[name].[hash:8].js", "bundle.js" | "[name].js" | "[chunkhash].js"。 9 // publicPath: "/assets/", // 輸出解析文件的目錄,設置之后所有資源文件會自動加上這個路徑,url地址是相對於HTML頁面的。 10 path: path.resolve(__dirname, "dist"), // path.join(__dirname,'dist'), // 輸出路徑,一般為絕對路徑。 11 chunkFilename: "[name].js", // 未被列入entry中,卻又需要被打包出來的文件命名配置。 12 library: "SimpleCesium", // 導出庫的名稱。 13 libraryTarget: "umd" // 導出庫的類型。常用umd模式,讓輸出的內容支持amd、commonJS模式加載。 14 }, // 輸出,webpack如何輸出結果的相關選項。 15 ... 16 }
各項參數含義請看注釋,或到webpack的官網查閱。上述nodEnv變量為編譯模式,需要自行要定義一下,path需要引用一下才能使用,如下:
1 const path = require("path"); // 路徑組件。 2 const nodeEnv = process.env.NODE_ENV; // 編譯模式。
做完上述配置后,運行build腳本生成一下,發現dist目錄下出現了simple-cesium.js文件,這說明項目已經成功生成了,但是一看大小才1KB,這明顯沒有把Cesium打包進來嘛。於是乎在index.js中添加Cesium引用:
1 // import * as Cesium from "/node_modules/cesium/Source/Cesium.js"; 2 import Viewer from "/node_modules/cesium/Source/Widgets/Viewer/Viewer.js";
我們先是直接引用Cesium的入口文件,引了這個文件基本就是把Cesium全家桶引進來了,然后運行build腳本,果不其然,3,055KB,這個大小可以用驚人來描述了。那么我們還是模塊化引用吧,於是我們再引入Viewer,這個應該算是Cesium中的必備的Widget了吧,build,結果大小為2,683KB,這也沒好哪里去嘛,只能說這個Viewer的家伙事兒也挺全的,但不管怎么樣大小跟全家桶還是有區別的,所以我們還是堅持模塊化引用吧。順便要提一下,使用import引入的模塊路徑是否添加.js后綴是有區別的,webpack會把它們當成兩個不同的模塊,如果代碼中剛好出現了用instanceof判斷A是否為B的實例的時候,就會掉進坑里。我的習慣還是所有模塊都加上.js后綴。
到這里為止,我們已經能將Cesium和項目文件一起打包了,接下來的目標就是運行起Cesium的三維界面。
為了動態引用JS腳本,我們要使用webpack-html-plugin插件,這個插件可以說是僅次於webpack-dev-server的存在,請看配置:
1 plugins: [ 2 new HtmlWebpackPlugin({ // 打包輸出HTML 3 title: 'Simple Cesium', // 給模板中的html注入標題,需要在模板的html中指明配置,<%= htmlWebpackPlugin.options.title %> 4 filename: 'index.html', // 指index定要生成的html路徑,基於輸出目錄。 5 template: 'public/index.html', // 指定html模板文件路徑。這里的模板類型可以是任意你喜歡的模板,可以是 html、jade、ejs、hbs,等等,但是要注意的是,使用自定義的模板文件時,需要提前安裝對應的loader,否則webpack不能正確解析。 6 inject: 'body', // 注入選項。1.true:默認值,script標簽位於html文件的body底部;2.body:script標簽位於html文件的body底部(同true);3.head:script標簽位於head標簽內;4.false:不插入生成的js文件,只是單純的生成一個html文件。 7 favicon: 'public/favicon.ico', // 給生成的html文件生成一個favicon,屬性值為favicon文件所在的路徑名。 8 hash: true, // 給生成的js文件一個獨特的hash值,該hash值是該次webpack編譯的hash值。默認值為false。 9 cache: true, // 默認是true,表示內容變化的時候是否生成一個新的文件。 10 showErrors: true, // 默認是true,作用是如果webpack編譯出現錯誤,webpack會將錯誤信息包裹在一個pre標簽內,屬性的默認值為 true,也就是顯示錯誤信息,開啟這個方便定位錯誤。 11 //chunks:['index','main'], // 主要用於多入口文件,當你有多個入口文件,編譯后生成多個打包后的文件,那么chunks就能選擇你要使用那些js文件,如果不設置則默認全部引入。 12 //excludeChunks:['main.js'], // 排除掉一些js。 13 minify: nodeEnv === 'production' ? { // 壓縮html文件。屬性值是一個壓縮選項或者false。默認值為false,即不對生成的html文件進行壓縮。 14 caseSensitive: true, // 是否對大小寫敏感,默認false。 15 collapseBooleanAttributes: true, // 是否簡寫boolean格式的屬性如:disabled="disabled" 簡寫為disabled 默認false。 16 collapseWhitespace: true, // 是否刪除空格與換行符,默認false。 17 minifyCSS: true, // 是否壓縮內聯css(使用clean-css進行的壓縮),默認值false。 18 minifyJS: true, // 是否壓縮html里的js(使用uglify-js進行的壓縮)。 19 preventAttributesEscaping: true, // 是否阻止屬性值轉義。 20 removeAttributeQuotes: true, // 是否移除屬性的引號,默認false。 21 removeComments: true, // 是否刪除注釋,默認false。 22 removeCommentsFromCDATA: true, // 是否從CDATA中刪除注釋,默認false。 23 removeEmptyAttributes: true, // 是否刪除空屬性,默認false。 24 removeOptionalTags: false, // 是否刪除可選的標簽,若開啟此項,生成的html中沒有body和head,html也未閉合。 25 removeRedundantAttributes: true, // 是否刪除多余的屬性。 26 removeScriptTypeAttributes: true, // 是否刪除script的類型屬性,在h5下面script的type默認值:text/javascript 默認值false。 27 removeStyleLinkTypeAttributes: true, // 是否刪除style的類型屬性,type="text/css" 同上。 28 useShortDoctype: true, // 是否使用短文檔類型,默認false。 29 } : {} 30 }) 31 ... 32 ],
我這注釋已經夠詳細了,相信已經不用多講了。上述minify參數主要是用來做壓縮的,可以選用默認設置或不設置,不用這么麻煩。
接着我們再改造一下index.js,增加內容:
const viewer = new Cesium.Viewer("cesiumContainer");
如果使用模塊方式引用的則改為如下寫法:
1 import Viewer from "/node_modules/cesium/Source/Widgets/Viewer/Viewer.js"; 2 const viewer = new Viewer("cesiumContainer");
然后運行start腳本調試,頁面並未正常顯示,控制欄報錯:DeveloperError: Unable to determine Cesium base URL automatically, try defining a global variable called CESIUM_BASE_URL。原來Cesium中有動態路徑存在,使用webpack打包后CESIUM_BASE_URL的值就取不到了。這種動態路徑的方式顯然不符合webpack的模塊式打包理念,但你又不能要求人家Cesium去改吧,所以只能自己改了。至於解決方法有以下幾種:
第一種,也是網上你能找到的最常見的解決方式,就是用DefinePlugin內置插件來重新定義CESIUM_BASE_URL的值。通常我們會把它定義為空,也就是默認的根路徑,但如果你想更換別的路徑,比如你想放到./libs目錄下,那么你在用的時候也要把你打包后的文件放置在這個目錄中,否則還是訪問不到的。先說一下這種方式的具體做法吧,在webapck.config.js中的plugins配置中增加:
1 plugins: [ 2 ... 3 new webpack.DefinePlugin({ 4 CESIUM_BASE_URL: JSON.stringify("") 5 }) 6 ],
這里面用到了webapck的內置插件,因此先要引入webapck:
const webpack = require('webpack'); // 訪問內置的插件
這種做法很簡單,唯一要注意的是字符串的值要使用JSON.stringify()來處理一下,否則是得不到預期的值的。
第二種,替換Cesium中的獲取動態路徑的方法,這種方法比較繁瑣,需要自定義插件,這里暫時不列出了。
CESIUM_BASE_URL配置完成后,再運行頁面,發現Cesium視窗已經能加載了,只是頁面樣式很亂,這是因為我們還未添加Cesium的內置CSS樣式。添加樣式引用:
import "/node_modules/cesium/Source/Widgets/widgets.css"
但是很不幸,又報錯了:
ERROR in ./node_modules/cesium/Source/Widgets/widgets.css 1:0 Module parse failed: Unexpected character '@' (1:0) You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.
這說的是它不識別“@”字符?我也是懵逼的,但腦中第一反應是要裝loader,果不其然,安裝style-loader和css-loader之后解決問題。安裝后要做如下配置:
1 module: { 2 rules: [ 3 ... 4 { 5 test: /\.css$/i, // 正則表達式,表示.css后綴的文件。 6 use: ['style-loader', 'css-loader'] // 針對css文件使用的loader,注意有先后順序,數組項越靠后越先執行。 7 } 8 ] 9 }
這樣應該就沒問題了。不過如果你的css中引用了圖片,最好配置一下url-loader,因為圖片文件也是需要loader的。安裝之后做一下配置:
1 module: { 2 rules: [ 3 ... 4 { 5 test: /\.(png|jpg|jpeg|gif|svg)$/, 6 loader: 'url-loader', 7 options: { 8 name: './Assets/sc/[name].[ext]', // 這個路徑其實是為了兼容Cesium的資源文件目錄 9 limit: 10240 // 超過10K的不轉換base64 10 } 11 } 12 ] 13 }
已經勝利在望了,不過還沒有結束,控制欄里仍然有好多404錯誤,這是因為Cesium的靜態資源沒有取到。上面我們講過了CESIUM_BASE_URL的基本原理,知道Cesium的靜態資源要放置於根目錄下的,因此我們要把資源都拷貝過來,使用的插件是copy-webpack-plugin,配置如下:
1 new CopyWebpackPlugin({ 2 patterns: [ 3 {from: path.join(cesiumSource, '../Build/Cesium/Workers'), to: 'Workers'}, 4 {from: path.join(cesiumSource, '../Build/Cesium/Assets'), to: 'Assets'}, 5 {from: path.join(cesiumSource, '../Build/Cesium/Widgets'), to: 'Widgets'}, 6 {from: path.join(cesiumSource, '../Build/Cesium/ThirdParty'), to: 'ThirdParty'} 7 ] 8 }), // 拷貝Cesium資源、控件、WebWorker到靜態目錄。
大家請看上面的寫法,跟你在網上看到的不一樣,新版本的webpack請使用這里是新寫法,否則報錯哦。另外,最近的Cesium版本中增加了ThirdParty資源目錄,某些高級應用中會使用到,因此也要將它拷貝過來。
至此,Cesium+webpack的搭建工作應該算告一段落了。運行一下腳本,發現黑黑的夜空已經出現在眼前了,但是地球呢,去哪里了?
首先,地球出不來的原因是影像數據沒有正確加載,在沒有指定影像數據的時候Cesium會默認從ION加載,而ION數據是需要token授權的。先去Cesium官網申請一個token,然后賦值。其次,還需要加載些3d模型,讓地圖更豐滿些,我們可以直接加載Cesium的OSM城市模型。把index.js整體改造一下:
1 import "/css/main.css"; 2 import "/node_modules/cesium/Source/Widgets/widgets.css"; 3 // import * as Cesium from "/node_modules/cesium/Source/Cesium.js"; 4 import Viewer from "/node_modules/cesium/Source/Widgets/Viewer/Viewer.js"; 5 import createOsmBuildings from "/node_modules/cesium/Source/Scene/createOsmBuildings.js"; 6 import Cartesian3 from "/node_modules/cesium/Source/Core/Cartesian3.js"; 7 import CesiumMath from "/node_modules/cesium/Source/Core/Math.js"; 8 import Ion from "/node_modules/cesium/Source/Core/Ion.js"; 9 10 const viewer = new Viewer("cesiumContainer", { 11 creditContainer: "creditContainer" 12 }); 13 viewer.scene.primitives.add(createOsmBuildings()); 14 viewer.scene.camera.flyTo({ 15 destination: Cartesian3.fromDegrees(-74.019, 40.6912, 750), 16 orientation: { 17 heading: CesiumMath.toRadians(20), 18 pitch: CesiumMath.toRadians(-20), 19 }, 20 }); 21 Ion.defaultAccessToken = 'Your Token';
再次運行調試,就出現了紐約的城市模型,但是影像還是沒出來,什么鬼?我也不知道是什么鬼,以前設置了token就可以了的。沒辦法,手工切換到OSM地圖源,總算加載出來了。但是總不至於每次手動加載吧,那太麻煩了,於是乎我們來點自動化的:
viewer.baseLayerPicker.viewModel.selectedImagery = viewer.baseLayerPicker.viewModel.imageryProviderViewModels[6];
上述代碼的意思讓底圖自動切換到第七個地圖源,即OpenStreetMap。
小結
至此,Cesium+webpack平台搭建完成。后續可能會做一些諸如分包之類的優化工作,弄好了之后我會直接在這里更新,敬請關注!另外,小伙伴們有空可以來群854943530逛逛,一定不虛此行哦。
-------------------- 不,這還不是我的底線 --------------------
補充
說好的后續優化來了。
首先,來做個分包。分包種類很多,但我只喜歡簡單粗暴的,就是分兩個包,一個程序包一個運行時包,對應到本項目就是:simple-cesium.js和simple-cesium.runtime.js。程序包負責打包所有自寫的代碼,運行時包則是負責打包第三方庫和一些通用庫。這樣做的目的是為了以最小的IO代價來進行版本更新,運行時包除非第三方庫升級否則基本不會變動的,那么每次更新版本只需要更新程序包即可,所以我們把Cesium庫也打包進運行時中。來看配置:
1 optimization: {
2 splitChunks: onePackage ? {} : { 3 chunks: "initial", // 從哪些chunks里面抽取代碼,還可以通過函數來過濾所需的chunks:"initial" | "async" | "all" | function。 4 minSize: 30000, // 抽取出來的文件在壓縮前的最小大小,默認為30000。 5 maxSize: 0, // 抽取出來的文件在壓縮前的最大大小,默認為0,表示不限制最大大小。 6 minChunks: 1, // 被引用次數,默認為1。如common中minChunks為2,表示將被兩次以上引用的代碼抽離成common。 7 maxAsyncRequests: 6, // 按需加載chunk的並發請求數量,默認為5。 8 maxInitialRequests: 4, // 頁面初始加載時的並發請求數量,默認為3。 9 automaticNameDelimiter: '~', // 抽取出來的文件的自動生成名字的分割符,默認為~。 10 cacheGroups: { 11 vendor: { 12 name: "simple-cesium.runtime", // 抽取出來文件的名字,表示自動生成文件名。 13 test: /[\\/](node_modules|thirdParty)[\\/]/, 14 chunks: "all", 15 priority: 10 // 優先級。 16 }, 17 common: { 18 name: "simple-cesium.common", 19 test: /[\\/]src[\\/]/, 20 minChunks: 2, 21 minSize: 0, // 如果被多次引用的通用代碼文件不超過minSize,則不會被抽離。 22 chunks: "all", 23 priority: 15, 24 reuseExistingChunk: true 25 } 26 } // 緩存組。 27 } 28 }
下面是打包后的目錄:
Cesium是個龐大的庫,所以打包后的文件遠不止這些,截圖只截取了一部分。不過我還是有些疑問,相同的配置,我在另外一個webpack的項目中打包出來就兩個文件,一個程序文件,一個運行時文件,也就是說所有的runtime都打包成一個文件了。於是網上苦尋答案,翻遍了竟然還是找不到究竟是哪個參數控制的,如果有大神知道,還請指點啊,畢竟運行時打包成一個文件也是有應用場景的。
其次,就是自動清理dist文件夾,這個也是有必要的,Cesium每次大版本更新里面就會有大批文件變更,如果每次手動去清理就太麻煩了。可以使用clean-webpack-plugin來解決這個問題,安裝后做如下配置:
1 plugins: [ 2 new CleanWebpackPlugin(), 3 ... 4 ]
這里要注意,它的引用方式也不一樣:
const {CleanWebpackPlugin} = require("clean-webpack-plugin");
暫時就補充這兩個吧,后續如果有別的優化還會繼續補充的。
最后,我把項目放到GitHub上了,后面我會以這個框架為起點繼續深化的,感興趣的小伙伴可以點個小星星持續關注一下哦。