1. 文件夾和文件夾內部文件的語義一致性
模塊文件夾應該保證單一入口和出口,怎么理解呢。
project |-- src |-- components |-- input |-- index.js |-- index.module.scss |-- pages |-- seller |-- components |-- input |-- index.js |-- index.module.scss |-- reducer.js |-- saga.js |-- index.js |-- index.module.scss |-- buyer |-- index.js |-- index.js
一般來說我們的目錄結構都會有一個文件夾是按照路由模塊來划分的,這個文件夾一般叫 pages 或者是 routes。這個文件夾里面應該包含我們項目所有的路由模塊(上面的例子路由模塊指的是 seller 和 buyer),並且僅應該包含路由模塊,而不應該有別的其他的非路由模塊的文件夾。
這樣做的好處在於一眼從 pages 文件夾看出這個項目的路由,比如上面的例子我們就可以說有 /seller
和/buyer
兩個路由,如果混入了其他非路由的文件夾就會混淆這里面都是路由文件夾的約定。
2. 單一入口/出口
接着上面的例子來講,seller 文件夾應該作為一個獨立的模塊由外部引入,並且 seller/index.js
應該作為外部引入 seller 模塊的唯一入口。換句話說,假如我有個 rootReducer 要引入 seller 的 reducer,我應該從 seller/index.js
文件引入,而不應該直接從 seller/reducer.js
引入。因為外部不應該知曉路由文件夾內部的文件分布,而是應該全部從 index.js
導入。同時 seller 內部文件不管如何分布,需要外部引入的都要在入口文件 index.js
導出。
// rootReducer.js // 錯誤用法 import sellerReducer from 'src/pages/seller/reducer' // 正確用法 import { reducer as sellerReducer } from 'src/pages/seller' // seller/index.js // 同時 import reducer from './reducer' export { reducer }
這樣做的好處在於,無論你的模塊文件夾內部有多亂,外部引用的時候,都是從一個入口文件引入,這樣就很好的實現了隔離,如果后續有重構需求,你就會發現這種方式的優點
3. 就近原則,緊耦合的文件應該放到一起,且應以相對路徑引用
繼續使用上面的例子,在 seller/index.js
中使用樣式,一般我們會用相對路徑的方式。這里為什么不用絕對路徑,可能有人說是寫起來方便,但是實際上這里用相對路徑可以保證這個 seller 模塊內部的獨立性。
// seller/index.js // 正確用法 import styles from './index.module.scss' // 錯誤用法 import styles from 'src/pages/seller/index.module.scss'
怎樣理解?我們現在的 seller 目錄是在 src/pages/seller
,如果我們后續發生了路由變更,需要加一個層級,變成 src/pages/user/seller
。如果我們采用第一種相對路徑的方式,那就可以直接將整個文件夾拖過去就好,seller 文件夾內部不需要做任何變更。但是如果我們采用第二種絕對路徑的方式,移動文件夾的同時,還需要對每個 import 的路徑做修改。
這樣做的好處?就是上面說的。
4. 公共的文件應該以絕對路徑的方式從根目錄引用
公共指的是多個路由模塊共用,上面的 src/components/input
就是一個公共組件 。如果我們要在 buyer 模塊使用這個組件可能有下面兩種引用方式
// buyer/index.js // 錯誤用法 import Input from '../../components/input' // 正確用法 import Input from 'src/components/input'
為什么?和上面的原因一樣,如果我們需要對文件夾結構進行調整。將 /src/components/input
變成 /src/components/new/input
,如果使用絕對路徑,只需要全局搜索替換。而如果使用相對路徑,則需要一個個找,很麻煩。
其實方便文件夾結構調整是次要的,主要的原因還是絕對路徑有全局的語義,相對路徑有獨立模塊的語義。這點會方便新上手的人熟悉這個模塊是共用的還是私有的。
5. /src 外的文件不應該被引入
這一點就比較好理解了,而且其實已經有腳手架做了相關的約束了,正常我們的前端項目都會有個 src 文件夾,里面放着所有的項目需要的資源,js, css, png, svg 等等。src 外會放一些項目配置,依賴,環境等文件。所以,src 文件夾外不應該放需要被引入的資源。
這樣的好處是方便划分項目代碼文件和配置文件
總結
項目的目錄結構很重要,因為目錄結構能體現很多東西,怎么規划目錄結構可能每個人有自己的理解。但是上面這些原則是我個人覺得是比較通用的,如果一個項目能遵循上面的原則,至少可以說,不會亂。
這里有兩個點我覺得是需要拎出來再強調一遍的:
- 一個是約定帶來的語義,上面的 5 點都體現了這點,約定帶來的語義可以很大程度減少我們理解項目的時間和難度
- 另一個就是面向重構編程,其實無論是使用 TypeScript 還是像上面的 3,4點一樣約定對模塊文件和公用文件的引用方式,都是為了在項目結構調整或者重構的時候,更快更安全的對文件進行修改。一個成熟的長期維護的項目,是一定要把重構的坑埋到代碼里面的。
后端服務器的組成: pom.xml(Maven項目配置文件) + java文件夾 + resource文件夾
- 代碼層(java),根目錄com.xxx: XxxApplication.java + 對應模塊代碼(domain + controller + service + mapper等)
- XxxApplication.java(項目主入口,main方法)
- controller: 控制層,請求接口
- service: 服務層,邏輯代碼, 數據服務的實現接口(serviceImpl)UserService.java 和 UserServiceImpl.java
- mapper: 數據層,或者dao, 比如UserMapper.java 、UserMapper.xml
-
domain: 實體類,同 bean、entity、model
bean: 任何一個java類都可以成為一個bean,這個類里包含對象的屬性、get、set方法及其他的業務邏輯。
model: model是MVC中的概念,可以理解為View層展示數據的對象。
entity:數據表對應到實體類的映射。
- 資源層(resource):存放資源文件,比如郵件html、mapper
- email郵件模板,比如registerSuccess.html
- properties配置文件,比如mybatis.properties
- mapper文件,比如UserMapper.xml(也可以寫到代碼層的mapper文件夾中)
- template模板
- application.yml
- log4j2.xml日志配置
// 根目錄結構 -src: -main: -java: - com.xxx -resource: - -test: -target: -pom.xml
- src/main/java: 代碼文件目錄
- src/main/resource: 資源文件目錄
- pom.xm:Maven項目配置文件
// java代碼文件目錄: 文件目錄按如下進行規范命名 -java: -com.xxx: -entity -controller -service -mapper -util -XxxApplication.java
- entity:實體類,也可以命名為bean、entity、model,例如User.java
- controller: 控制層,請求接口,例如UserController.java
- service: 服務層,以及關聯的接口文件,例如UserService.java(impl/UserServiceImpl.java)
- mapper: 數據層,也可以命名為dao,例如UserMapper.java和UserMapper.xml
- model: 請求使用到的實體類: xxxRequestTO.java、xxxReponseTO.java
- config、constant、util等配置文件
- XxxApplication.java : 項目主入口,main方法
// resource資源文件目錄 -resource: -mapper -static -template -application.yml
- application.yml: 配置文件,也可命名為application.properties
// 數據庫配置 -- application.yml spring: datosource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/dataBaseName?userUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8 username: youMysqlUsername password: yourMysqlPassword
vue架構
vue項目目錄結構詳解
項目簡介
基於 vue.js 的前端開發環境,用於前后端分離后的單頁應用開發,可以在開發時使用 ES Next、scss 等最新語言特性。項目包含:
基礎庫: vue.js、vue-router、vuex、whatwg-fetch
編譯/打包工具:webpack、babel、node-sass
單元測試工具:karma、mocha、sinon-chai
本地服務器:express
目錄結構
├── README.md 項目介紹
├── index.html 入口頁面
├── build 構建腳本目錄
│ ├── build-server.js 運行本地構建服務器,可以訪問構建后的頁面
│ ├── build.js 生產環境構建腳本
│ ├── dev-client.js 開發服務器熱重載腳本,主要用來實現開發階段的頁面自動刷新
│ ├── dev-server.js 運行本地開發服務器
│ ├── utils.js 構建相關工具方法
│ ├── webpack.base.conf.js wabpack基礎配置
│ ├── webpack.dev.conf.js wabpack開發環境配置
│ └── webpack.prod.conf.js wabpack生產環境配置
├── config 項目配置
│ ├── dev.env.js 開發環境變量
│ ├── index.js 項目配置文件
│ ├── prod.env.js 生產環境變量
│ └── test.env.js 測試環境變量
├── mock mock數據目錄
│ └── hello.js
├── package.json npm包配置文件,里面定義了項目的npm腳本,依賴包等信息
├── src 源碼目錄
│ ├── main.js 入口js文件
│ ├── app.vue 根組件
│ ├── components 公共組件目錄
│ │ └── title.vue
│ ├── assets 資源目錄,這里的資源會被wabpack構建
│ │ └── images
│ │ └── logo.png
│ ├── routes 前端路由
│ │ └── index.js
│ ├── store 應用級數據(state)
│ │ └── index.js
│ └── views 頁面目錄
│ ├── hello.vue
│ └── notfound.vue
├── static 純靜態資源,不會被wabpack構建。
└── test 測試文件目錄(unit&e2e)
└── unit 單元測試
├── index.js 入口腳本
├── karma.conf.js karma配置文件
└── specs 單測case目錄
└── Hello.spec.js
環境安裝
本項目依賴 node.js, 使用前先安裝 node.js 和 cnpm(顯著提升依賴包的下載速度)。
自行下載並安裝 node.js: https://nodejs.org/en/download/
然后安裝 cnpm 命令:
npm install -g cnpm --registry=https://registry.npm.taobao.org
快速開始
git clone https://github.com/hanan198501/vue-spa-template.git
cd vue-spa-template
cnpm install
npm run dev
命令列表:
開啟本地開發服務器,監控項目文件的變化,實時構建並自動刷新瀏覽器,瀏覽器訪問 http://localhost:8081
npm run dev
使用生產環境配置構建項目,構建好的文件會輸出到 "dist" 目錄,
npm run build
運行構建服務器,可以查看構建的頁面
npm run build-server
運行單元測試
npm run unit
前后端分離
項目基於 spa 方式實現前后端分離,服務器通過 nginx 區分前端頁面和后端接口請求,分發到不同服務。前端物理上只有一個入口頁面, 路由由前端控制(基於vue-router),根據不同的 url 加載相應數據和組件進行渲染。
接口 mock
前后端分離后,開發前需要和后端同學定義好接口信息(請求地址,參數,返回信息等),前端通過 mock 的方式,即可開始編碼,無需等待后端接口 ready。 項目的本地開發服務器是基於 express 搭建的,通過 express 的中間件機制,我們已經在 dev-server 中添加了接口 mock 功能。 開發時,接口的 mock 數據統一放在 mock 目錄下,每個文件內如下:
module.exports = {
// 接口地址
api: '/api/hello',
// 返回數據 參考http://expressjs.com/zh-cn/4x/api.html
response: function (req, res) {
res.send(`
<p>hello vue!</p>
`);
}
}
模塊化
開發時可以使用 ES2015 module 語法,構建時每個文件會編譯成 amd 模塊。
組件化
整個應用通過 vue 組件的方式搭建起來,通過 vue-router 控制相應組件的展現,組件樹結構如下:
app.vue 根組件(整個應用只有一個)
├──view1.vue 頁面級組件,放在 views 目錄里面,有子組件時,可以建立子目錄
│ ├──component1.vue 功能組件,公用的放在 components 目錄,否則放在 views 子目錄
│ ├──component2.vue
│ └──component3.vue
├──view2.vue
│ ├──component1.vue
│ └──component4.vue
└──view3.vue
├──component5.vue
……
單元測試
可以為每個組件編寫單元測試,放在 test/unit/specs 目錄下面, 單元測試用例的目錄結構建議和測試的文件保持一致(相對於src),每個測試用例文件名以 .spec.js結尾。 執行 npm run unit 時會遍歷所有的 spec.js 文件,產出測試報告在 test/unit/coverage 目錄。
聯調方式
前后端分離后,由於服務端和前端的開發環境處於2台不同的機器上,前端的異步請求需要代理到后端機器中。 聯調的時候,只需通過 proxy 參數運行 dev 腳本即可,所有 mock 目錄下定義的接口將會轉發到 proxy 參數指定的機器:
172.16.36.90:8083 為后端機器的環境地址
npm run dev -- --proxy=172.16.36.90:8083
這樣,如果 mock 目錄下有定義了接口 /api/hello ,將會轉發到 http://172.16.36.90/:8083/api/hello