摘要:背景隨著互聯(lián)網(wǎng)應(yīng)用工程規(guī)模的日益復(fù)雜化和精細(xì)化,我們在開發(fā)一個標(biāo)準(zhǔn)應(yīng)用的早已開始告別單干模式,為了提升開發(fā)效率,前后端分離的需求越來越被重視,前端負(fù)責(zé)展現(xiàn)交互邏輯,后端負(fù)責(zé)業(yè)務(wù)數(shù)據(jù)接口,基本上也成為了我們?nèi)粘m椖糠止ぶ械臉?biāo)配,但是前后端分離
背景
隨著互聯(lián)網(wǎng)應(yīng)用工程規(guī)模的日益復(fù)雜化和精細(xì)化,我們在開發(fā)一個標(biāo)準(zhǔn)web應(yīng)用的早已開始告別單干模式,為了提升開發(fā)效率,前后端分離的需求越來越被重視,前端負(fù)責(zé)展現(xiàn)/交互邏輯,后端負(fù)責(zé)業(yè)務(wù)/數(shù)據(jù)接口,基本上也成為了我們?nèi)粘m椖糠止ぶ械臉?biāo)配,但是前后端分離一直以來都是一個工程概念,每個團(tuán)隊在實現(xiàn)工程中都會基于自身的技術(shù)棧選擇和開發(fā)環(huán)境進(jìn)行具體的實現(xiàn),本文便根據(jù)自身團(tuán)隊在webapck開發(fā)中搭建的前后端分離開發(fā)環(huán)境進(jìn)行部分?jǐn)⑹觥?/p> 理想化的前后端分離環(huán)境
目前業(yè)界比較有代表性的前后端分離的例子是SPA(Single-page application),所有用到的展現(xiàn)數(shù)據(jù)都是后端通過異步接口(AJAX/JSONP/WEBSOCKET)的方式提供的,現(xiàn)如今最火的前端框架如:React, Vue,Angular等也都推薦采用SPA的模式進(jìn)行開發(fā)并且從組件化,數(shù)據(jù)流,狀態(tài)容器再到網(wǎng)絡(luò)請求,單頁路由等都給出了完善的全家桶方案。從某種意義上來說,SPA確實做到了前后端分離,但這種方式存在如下幾個亟待問題:
前端開發(fā)本地開發(fā)環(huán)境下該如何突破域的限制和服務(wù)端接口進(jìn)行通信?
一條命令,能否同時完成webpack和node sever兩個進(jìn)程的啟動?
開發(fā)環(huán)境下的前端資源路徑應(yīng)該如何配置?
mock數(shù)據(jù)應(yīng)該怎么做?
打包構(gòu)建后的文件能否直接預(yù)覽效果?
針對以上的問題,我們來看看怎樣利用webpack現(xiàn)有的一些機制和借助node的環(huán)境搭配來進(jìn)行逐個擊破,具體設(shè)計如下:
由此可見,我們理想化的開發(fā)環(huán)境應(yīng)根據(jù)具備以下幾點要求:
操作夠簡單,拉下代碼后,只需要記住僅有的幾個命令就能直接進(jìn)入開發(fā)狀態(tài)
解耦夠徹底,開發(fā)者只需要修改路由配置表就能無縫在多個請求接口中靈活切換
資源夠清晰,所有的開發(fā)資源都能到精確可控,同時支持一鍵打包構(gòu)建,單頁和多頁模式可并存
配置夠靈活,可以根據(jù)自身項目的實際情況靈活添加各類中間件,擴展模塊和第三方插件
不得不提的webpack-dev-serverwebpack本身的定位是一個資源管理和打包構(gòu)建工作,本身的強大之處在于對各種靜態(tài)資源的依賴分析和預(yù)編譯,在實際開發(fā)中官方還推薦了一個快速讀取webpack配置的server環(huán)境webpack-dev-server,官方的介紹是:"Use webpack with a development server that provides live reloading. The webpack-dev-server is a little Node.js Express server, which uses the webpack-dev-middleware to serve a webpack bundle. It also has a little runtime which is connected to the server via Sock.js.",一個適用于開發(fā)環(huán)境的,基于express + webpack-dev-middleware實現(xiàn)的,支持實時更新,內(nèi)存構(gòu)建資源的開發(fā)服務(wù)器,通過簡單的配置即可滿足webpack開發(fā)環(huán)境中的一系列需求,但是當(dāng)我們的開發(fā)環(huán)境日趨復(fù)雜和多樣的時候,不僅需要對自定義配置的細(xì)節(jié)靈活可控,同時需要對進(jìn)行加入各種第三方的插件進(jìn)行功能擴展,才能最大程度的發(fā)揮webpack環(huán)境中的威力。
打造項目專屬的前端開發(fā)環(huán)境有了理想環(huán)境下的的述求,也了解到了webpack-dev-server的實現(xiàn)精髓,那么,我們就可以一步步地來打造專屬自身的開發(fā)環(huán)境:
一 、借助node和http-proxy實現(xiàn)跨域通信前后端分離開發(fā)中,本地前端開發(fā)調(diào)用接口會有跨域問題,一般有以下幾種解決方法:
直接啟動服務(wù)端項目,再將項目中的資源url指向到前端服務(wù)中的靜態(tài)資源地址,好處在于因為始終在服務(wù)端的環(huán)境中進(jìn)行資源調(diào)試,不存在接口的跨域訪問問題,但是缺陷也比較明顯,需要同時啟動兩套環(huán)境,還需要借助nginx,charles等工具進(jìn)行資源地址的代理轉(zhuǎn)發(fā),配置比較繁瑣,對開發(fā)者對網(wǎng)絡(luò)的理解和環(huán)境配置要求較高,資源開銷也大;
CORS跨域:后端接口在返回的時候,在header中加入"Access-Control-Allow-origin":* 等配置,利用跨域資源共享實現(xiàn)跨域,前端部分只要求支持xhr2標(biāo)準(zhǔn)的瀏覽器,但是服務(wù)端在請求頭中需要在header中做響應(yīng)頭配置,在一定程度上還是對服務(wù)端的接口設(shè)置有一定的依賴;
http-proxy:用nodejs搭建本地http服務(wù)器,并且判斷訪問接口URL時進(jìn)行轉(zhuǎn)發(fā),由于利用了http-proxy代理的模式進(jìn)行了轉(zhuǎn)發(fā),采用的是服務(wù)對服務(wù)的模式,能較為完美解決本地開發(fā)時候的跨域問題,也是本文中推薦的方式,配置如下:
1、搭建node和http-proxy環(huán)境
npm install express # express作為node基礎(chǔ)服務(wù)框架 npm install http-proxy-middleware # http-proxy的express中間件 npm install body-parser # bodyParser中間件用來解析http請求體 npm install querystring # querystring用來字符串化對象或解析字符串
工程項目下可以新建一個server的文件夾放置node資源,如下所示:
server ├── main.js ├── proxy.config.js ├── routes └── views
2、編寫代理配置腳本:
proxy.config.js中可以配置對應(yīng)需要代理的url和目標(biāo)url,如下:
const proxy = [ { url: "/back_end/auth/*", target: "http://10.2.0.1:8351" }, { url: "/back_end/*", target: "http://10.2.0.1:8352" } ]; module.exports = proxy;
main.js中的配置如下:
const express = require("express") const bodyParser = require("body-parser") const proxy = require("http-proxy-middleware") const querystring = require("querystring") const app = express() // make http proxy middleware setting const createProxySetting = function (url) { return { target: url, changeOrigin: true, headers: { Accept: "application/json", "X-Requested-With": "XMLHttpRequest" }, onProxyReq: function (proxyReq, req) { if (req.method === "POST" && req.body) { const bodyData = querystring.stringify(req.body) proxyReq.write(bodyData) } } } } // parse application/json app.use(bodyParser.json()) // parse application/x-www-form-urlencoded app.use(bodyParser.urlencoded({ extended: false })) // proxy proxyConfig.forEach(function (item) { app.use(item.url, proxy(createProxySetting(item.target))) }) // eg: http://127.0.0.1:3000/back_end/oppor => http://10.2.0.1:8352/back_end/oppor
通過以上的配置我們就能輕松將指定url下的請求自動轉(zhuǎn)發(fā)到匹配成功的目標(biāo)接口下。
> NODE_ENV=development node ./server/main.js isDebug: true [HPM] Proxy created: / -> http://10.2.0.1:8351 [HPM] Proxy created: / -> http://10.2.0.1:8352 Listening at 192.168.1.104:3000 webpack built d558389f7a9a453af17f in 2018ms Hash: d558389f7a9a453af17f Version: webpack 1.14.0 Time: 2018ms二、將webpack配置和node server進(jìn)程打通
1、解耦webpack中的配置
由于webpack在開發(fā)和生產(chǎn)環(huán)境中經(jīng)常需要做各種配置的切換,官方也提供了DefinePlugin來進(jìn)行環(huán)境參數(shù)設(shè)置,但是大量的判斷語句侵入webpack.config中其實會導(dǎo)致代碼的可讀性和復(fù)用性變差,也容易造成代碼冗余,我們在此可以對配置文件進(jìn)行重構(gòu),將之前的webpack配置文件拆解成了webpack.config.js,project.config.js和environments.config.js三個文件,三個文件各司其職,又可互相協(xié)作,減少維護(hù)成本,如下:
environments.config.js: 主要的作用就是存放在特定環(huán)境下的需要變化的配置參數(shù),包含有:publicpath, devtools, wanings,hash等
project.config.js:主要的作用是存放于項目有關(guān)的基礎(chǔ)配置,如:server,output,loader,externals,plugin等基礎(chǔ)配置;通過一個overrides實現(xiàn)對environments中的配置信息重載。
webpack.config.js:主要是讀取project.config.js中的配置,再按標(biāo)準(zhǔn)的webpack字段填入project中的配置信息,原則上是該文件的信息只與構(gòu)建工具有關(guān),而與具體的項目工程無關(guān),可以做到跨項目間復(fù)用。
config ├── environments.config.js ├── project.config.js └── webpack.config.js
environments.config.js中的關(guān)鍵實現(xiàn):
// Here is where you can define configuration overrides based on the execution environment. // Supply a key to the default export matching the NODE_ENV that you wish to target, and // the base configuration will apply your overrides before exporting itself. module.exports = { // ====================================================== // Overrides when NODE_ENV === "development" // ====================================================== development : (config) => ({ compiler_public_path : `http://${config.server_host}:${config.server_port}/` }), // ====================================================== // Overrides when NODE_ENV === "production" // ====================================================== production : (config) => ({ compiler_base_route : "/apps/", compiler_public_path : "/static/", compiler_fail_on_warning : false, compiler_hash_type : "chunkhash", compiler_devtool : false, compiler_stats : { chunks : true, chunkModules : true, colors : true } }) }
project.config.js中的關(guān)鍵實現(xiàn):
// project.config.js const config = { env : process.env.NODE_ENV || "development", // ---------------------------------- // Project Structure // ---------------------------------- path_base : path.resolve(__dirname, ".."), dir_client : "src", dir_dist : "dist", dir_public : "public", dir_server : "server", dir_test : "tests", // ---------------------------------- // Server Configuration // ---------------------------------- server_host : ip.address(), // use string "localhost" to prevent exposure on local network server_port : process.env.PORT || 3000, // ---------------------------------- // Compiler Configuration // ---------------------------------- compiler_devtool : "source-map", compiler_hash_type : "hash", compiler_fail_on_warning : false, compiler_quiet : false, compiler_public_path : "/", compiler_stats : { chunks : false, chunkModules : false, colors : true } }; // 在此通過讀取環(huán)境變量讀取environments中對應(yīng)的配置項,對前面的配置項進(jìn)行覆蓋 const environments = require("./environments.config") const overrides = environments[config.env] if (overrides) { debug("Found overrides, applying to default configuration.") Object.assign(config, overrides(config)) } else { debug("No environment overrides found, defaults will be used.") } module.exports = config
webpack.config.js中的關(guān)鍵實現(xiàn):
const webpack = require("webpack") const project = require("./project.config") const debug = require("debug")("app:config:webpack") const UglifyJsParallelPlugin = require("webpack-uglify-parallel") const __DEV__ = project.globals.__DEV__ const __PROD__ = project.globals.__PROD__ const webpackConfig = { name : "client", target : "web", devtool : project.compiler_devtool, resolve : { modules: [project.paths.client(), "node_modules"], extensions: [".web.js", ".js", ".jsx", ".json"] }, module : {} } if (__DEV__) { debug("Enabling plugins for live development (HMR, NoErrors).") webpackConfig.plugins.push( new webpack.HotModuleReplacementPlugin() ) } else if (__PROD__) { debug("Enabling plugins for production (UglifyJS).") webpackConfig.plugins.push( new webpack.optimize.OccurrenceOrderPlugin(), new webpack.optimize.DedupePlugin(), new UglifyJsParallelPlugin({ workers: os.cpus().length, mangle: true, compressor: { warnings: false, drop_debugger: true, dead_code: true } }) ) }
由此可知,三者間的注入關(guān)系如下:
environments -> project -> webpack
2、整合webpack在開發(fā)環(huán)境中依賴的中間件
參考webapck-dev-server中的實現(xiàn),我們可以將webpack-dev-middleware和webpack-hot-middleware加入到我們的express配置中,
npm install webpack-dev-middleware npm install webpack-hot-middleware
具體配置如下:
const express = require("express") const debug = require("debug")("app:server") const webpack = require("webpack") const webpackConfig = require("../config/webpack.config") const project = require("../config/project.config") const app = express() // ------------------------------------ // Apply Webpack HMR Middleware // ------------------------------------ if (project.env === "development") { const compiler = webpack(webpackConfig) debug("Enabling webpack dev and HMR middleware") app.use(require("webpack-dev-middleware")(compiler, { publicPath : webpackConfig.output.publicPath, contentBase : project.paths.client(), hot : true, quiet : project.compiler_quiet, noInfo : project.compiler_quiet, lazy : false, stats : project.compiler_stats })) // webpack_hmr app.use(require("webpack-hot-middleware")(compiler, { path: "/__webpack_hmr" })) // proxy ....... } module.exports = app.listen(project.server_port, function (err) { if (err) { console.log(err) return } var uri = project.server_host + ":" + project.server_port console.log("Listening at " + uri + " ") });
這樣當(dāng)我們執(zhí)行下述的時候,就能一鍵完成webpack基礎(chǔ)配置,熱更新以及epxress服務(wù)的啟動,并且可以完全根據(jù)express的配置說明來自定義擴展我們的前端開發(fā)資源。
ENV=development node ./bin/dev-server.js三、前端資源路徑設(shè)計
實際開發(fā)中,所有涉及到的前端資源我們進(jìn)行歸類一般會有如下幾種:
html:html頁面,結(jié)合到服務(wù)后一般稱為模板資源,是所有資源的入口和結(jié)果呈現(xiàn)頁;
js:javascript執(zhí)行腳本資源,基于現(xiàn)代Javascript框架開發(fā)后通常還需要借助babel,typescript等進(jìn)行編譯處理,分為build前后build后兩套代碼;
css:樣式資源,如果采用了less,sass等工具處理后會也會從.less和.sass編譯成.css文件;
static: 靜態(tài)資源,通常會包含有font,image,audio,video等靜態(tài)文件,結(jié)合到服務(wù)框架中一般需要設(shè)定特定的訪問路徑,直接讀取文件加載。
在wepback的配置中,前端資源路徑我們通常是借助path和publicPath
對構(gòu)建出來的前端資源進(jìn)行索引,由于webpack采用了基于內(nèi)存構(gòu)建的方式,path通常用來用來存放打包后文件的輸出目錄,publicPath則用來指定資源文件引用的虛擬目錄,具體示例如下:
module.exports = { entry: path.join(__dirname,"src","entry.js"), output: { /* webpack-dev-server環(huán)境下,path、publicPath、--content-base 區(qū)別與聯(lián)系 path:指定編譯目錄而已(/build/js/),不能用于html中的js引用。 publicPath:虛擬目錄,自動指向path編譯目錄(/assets/ => /build/js/)。html中引用js文件時,必須引用此虛擬路徑(但實際上引用的是內(nèi)存中的文件,既不是/build/js/也不是/assets/)。 --content-base:必須指向應(yīng)用根目錄(即index.html所在目錄),與上面兩個配置項毫無關(guān)聯(lián)。 ================================================ 發(fā)布至生產(chǎn)環(huán)境: 1.webpack進(jìn)行編譯(當(dāng)然是編譯到/build/js/) 2.把編譯目錄(/build/js/)下的文件,全部復(fù)制到/assets/目錄下(注意:不是去修改index.html中引用bundle.js的路徑) */ path: path.join(__dirname,"build","js"), publicPath: "/assets/", //publicPath: "http://cdn.com/assets/",//你也可以加上完整的url,效果與上面一致(不需要修改index.html中引用bundle.js的路徑,但發(fā)布生產(chǎn)環(huán)境時,需要使用插件才能批量修改引用地址為cdn地址)。 filename: "bundle.js" } };
有了如上的概念,我們就可以將path,publicpath和express中的配置結(jié)合起來,同時由于在開發(fā)環(huán)境中我們的的資源入口通常又會按特定的目錄來進(jìn)行文件存放,如下圖所示:
project ├── LICENSE ├── README.md ├── app.json ├── dist ├── bin ├── config ├── package.json ├── postcss.config.js ├── public ├── server ├── src └── yarn.lock
從中不難發(fā)現(xiàn)node server中需要配置的資源目錄往往又會和webpack的工程目錄重疊,那么我們就需要在express中進(jìn)行相應(yīng)的配置,才能實現(xiàn)資源的正確索引。
1、html模板資源讀取
html作為webpack中的templates,在express中則會變成views,讀取方式會發(fā)生變化,所以我們需要對資源進(jìn)行如下配置:
npm install ejs #讓express支持html模板格式
const ejs = require("ejs") const app = express() // view engine, 默認(rèn)可以指向template app.set("views", project.paths.template()) app.engine(".html", ejs.__express) app.set("view engine", "html") // 通過配置讓express讀取webpack的內(nèi)存打包資源下的template文件 app.use("/home", function (req, res, next) { const filename = path.join(compiler.outputPath, "index.html"") compiler.outputFileSystem.readFile(filename, (err, result) => { if (err) { return next(err) } res.set("content-type", "text/html") res.send(result) res.end() }) }) //讓express所有的路由請求都落到index.html中,再有前端框架中的前端路由接管頁面的跳轉(zhuǎn) app.use("*", function (req, res, next) { const filename = path.join(compiler.outputPath, "index.html") compiler.outputFileSystem.readFile(filename, (err, result) => { if (err) { return next(err) } res.set("content-type", "text/html") res.send(result) res.end() }) /*也可以指定到特定的views文件下進(jìn)行模板資源讀取*/ res.render("home.html", { name:"home.html" }) })
2、js和css資源讀取
js和css的引用地址在webpack的開發(fā)環(huán)境中通常會指向publicpath,所以在開發(fā)頁面中會直接如下嵌入如下地址,由于是采用絕對地址指向,所以無需做任何配置:
3、靜態(tài)資源讀取
其他類似font,images等靜態(tài)讀取,我們可以將一個圖片放到工程結(jié)構(gòu)中的public下,則訪問地址可以按如下書寫,支持真實路徑和虛擬路徑:
// 真實路徑,根目錄訪問:/demo.png -> /pulbic/demo.png app.use(express.static(project.paths.public())) // 真實路徑,子目錄訪問:/static/demo.png -> /pulbic/static/demo.png app.use(express.static(project.paths.public())) // 虛擬路徑,跟目錄訪問:/static/demo.png -> /pulbic/demo.png app.use("/static/", express.static(project.paths.public())) // 虛擬路徑,子目錄訪問:/static/img/demo.png -> /pulbic/img/demo.png app.use("/static/", express.static(project.paths.public()))
通過以上配置,我們就可以在訪問開發(fā)地址( eg: localhost:3000 )時即可得到所需的全部前端資源。
四、mock數(shù)據(jù)模擬作為前端經(jīng)常需要模擬后臺數(shù)據(jù),我們稱之為mock。通常的方式為自己搭建一個服務(wù)器,返回我們想要的數(shù)據(jù),既然我們已經(jīng)將express集成到了我們的開發(fā)環(huán)境下,那么實現(xiàn)一個mock就會非常簡單,以下介紹兩種mock數(shù)據(jù)的方式。
1、配置專屬的mock路由模塊
我們可以在我們的server項目下的routes模塊中加入一個mock模塊,如下所示:
server ├── main.js ├── mock │?? ├── opporList.json ├── routes │?? ├── index.js │?? └── mock.js └── views └── home.html
然后再在我們的server下的配置文件中導(dǎo)入mock模塊配置:
// main.js const mock = require("./routes/mock") if (project.env === "development") { // mock routes app.use("/mock, mock) }
routes中的mock.js中寫入如下mock數(shù)據(jù)配置即可:
const express = require("express") const router = express.Router() const opporList = require("../mock/opporList.json"); const Mock = require("mockjs"); // 直接讀取json文件導(dǎo)出 router.get("/backend/opporList", function (req, res) { res.json(opporList) }) // 基于mockjs生成數(shù)據(jù), 優(yōu)勢在于對項目代碼無侵入,并且支持fetch,xhr等多種方式的攔截 router.get("/backend/employee", function (req, res) { var data = Mock.mock({ // 屬性 list 的值是一個數(shù)組,其中含有 1 到 10 個元素 "list|1-10": [{ // 屬性 id 是一個自增數(shù),起始值為 1,每次增 1 "id|+1": 1 }] }) res.json(data) }) module.exports = router
配置完成后,訪問如下地址即可拿到mock數(shù)據(jù):
http://127.0.0.1:3000/mock/ba...
http://127.0.0.1:3000/mock/ba...
再利用我們的proxy.config修改node-proxy配置,將測試自動轉(zhuǎn)到mock目標(biāo)地址下:
const proxy = [ { url: "/backend/*", target: "http://127.0.0.1:3000/mock" } ] module.exports = proxy
2、搭建獨立的mock服務(wù)
如果企業(yè)中有部署獨立的mock服務(wù)器,如puer+mock:我們也可以通過修改簡單的proxy.config來直接實現(xiàn)需要mock的請求地址轉(zhuǎn)發(fā),相對修改就比較簡單,如下:
const proxy = [ { url: "/backend/*", target: "http://10.4.31.11:8080/mock" } ] module.exports = proxy五、預(yù)覽打包后的資源效果
當(dāng)我們開發(fā)完成后,wepback通過編譯可以得到我們需要的各種靜態(tài)資源,這類文件通常是作為靜態(tài)資源存在,需要放到cdn或者部署到服務(wù)器上才能訪問,但是我們通過簡單的配置也可以直接在本地環(huán)境下直接預(yù)覽打包后的資源效果,具體操作如下:
1. 找到構(gòu)建資源生成目錄, 確認(rèn)構(gòu)建資源已存在:
dist
├── css
│?? ├── app.5f5af15a.css
│?? ├── login.7cb6ada6.css
│?? └── vendors.54895ec1.css
├── images
│?? ├── login_bg.8953d181.png
│?? ├── logo.01cf3dce.png
│?? └── wap_ico.e4e9be83.png
├── index.html
├── js
│?? ├── app.eb852be2.js
│?? ├── login.9a049514.js
│?? ├── manifest.c75a01fc.js
│?? └── vendors.20a872dc.js
└── login.html
2. 修改express的文本配置信息,加入構(gòu)建完成后的靜態(tài)資源地址配置:
app.set("views", project.paths.dist()) if (project.env === "development") { .... } else { debug( "Server is being run outside of live development mode" ) // 配置預(yù)覽環(huán)境下的proxy.config,一般可以指向測試環(huán)境地址 const proxyConfig = require("./proxy.test.config") const routes = require("./routes") proxyConfig.forEach(function (item) { app.use(item.url, proxy(createProxySetting(item.target))) }) // 修改靜態(tài)資源指向地址,可以直接配置到dist目錄下 app.use(project.compiler_public_path,express.static(project.paths.dist()) // 配置訪問路由url,并在設(shè)置置真實的template文件地址,與webpack中的htmlplugin下的filename配置路徑保持一致,一般都在dist目錄下 app.use(project.compiler_base_route, routes) }
3. 啟動預(yù)覽頁面,訪問:localhost:3000即可
NODE_ENV=production node ./server/main.js完整工程結(jié)構(gòu)目錄結(jié)構(gòu)參考
Project
├── LICENSE
├── README.md
├── app.json
├── bin
│?? ├── compile.js
│?? └── dev-server.js
├── config
│?? ├── environments.config.js
│?? ├── karma.config.js
│?? ├── npm-debug.log
│?? ├── project.config.js
│?? └── webpack.config.js
├── package.json
├── postcss.config.js
├── public
│?? ├── favicon.ico
│?? ├── humans.txt
│?? └── robots.txt
├── server
│?? ├── main.js
│?? ├── proxy.config.js
│?? ├── routes
│?? └── views
├── src
│?? ├── api
│?? ├── components
│?? ├── containers
│?? ├── index.html
│?? ├── layouts
│?? ├── main.js
│?? ├── routes
│?? ├── static
│?? ├── store
│?? └── until
├── tests
│?? ├── components
│?? ├── layouts
│?? ├── routes
│?? ├── store
│?? └── test-bundler.js
└── yarn.lock
工程演示demo
小結(jié)將webpack的各類高級特性和node基礎(chǔ)服務(wù)有效相結(jié)合,按需打造專屬自身項目的開發(fā)平臺,不僅能將項目體系從簡單的頁面開發(fā)轉(zhuǎn)向工程化標(biāo)準(zhǔn)邁進(jìn),更能極大的改善前端開發(fā)的體驗,提升開發(fā)效率。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/91678.html
摘要:前端基礎(chǔ)架構(gòu)和硬核介紹技術(shù)棧的選擇首先我們構(gòu)建前端架構(gòu)需要對前端生態(tài)圈有一切了解,并且最好帶有一定的技術(shù)前瞻性,好的技術(shù)架構(gòu)可能日后會方便的擴展,減少重構(gòu)的次數(shù),即使重構(gòu)也不需要大動干戈,我通常選型技術(shù)棧會參考以下三點一提出自身業(yè)務(wù)的需求是 # 前端基礎(chǔ)架構(gòu)和硬核介紹 showImg(https://segmentfault.com/img/remote/146000001626972...
摘要:前端基礎(chǔ)架構(gòu)和硬核介紹技術(shù)棧的選擇首先我們構(gòu)建前端架構(gòu)需要對前端生態(tài)圈有一切了解,并且最好帶有一定的技術(shù)前瞻性,好的技術(shù)架構(gòu)可能日后會方便的擴展,減少重構(gòu)的次數(shù),即使重構(gòu)也不需要大動干戈,我通常選型技術(shù)棧會參考以下三點一提出自身業(yè)務(wù)的需求是 # 前端基礎(chǔ)架構(gòu)和硬核介紹 showImg(https://segmentfault.com/img/remote/146000001626972...
摘要:前端準(zhǔn)備前端了解過關(guān)了嗎前端基礎(chǔ)架構(gòu)和硬核介紹技術(shù)棧的選擇首先我們構(gòu)建前端架構(gòu)需要對前端生態(tài)圈有一切了解,并且最好帶有一定的技術(shù)前瞻性,好的技術(shù)架構(gòu)可能日后會方便的擴展,減少重構(gòu)的次數(shù),即使重構(gòu)也不需要大動干戈,我通常選型技術(shù)棧會參考以下三 # 前端準(zhǔn)備 :前端了解過關(guān)了嗎?前端基礎(chǔ)架構(gòu)和硬核介紹 showImg(https://segmentfault.com/img/remote/...
摘要:前端準(zhǔn)備前端了解過關(guān)了嗎前端基礎(chǔ)架構(gòu)和硬核介紹技術(shù)棧的選擇首先我們構(gòu)建前端架構(gòu)需要對前端生態(tài)圈有一切了解,并且最好帶有一定的技術(shù)前瞻性,好的技術(shù)架構(gòu)可能日后會方便的擴展,減少重構(gòu)的次數(shù),即使重構(gòu)也不需要大動干戈,我通常選型技術(shù)棧會參考以下三 # 前端準(zhǔn)備 :前端了解過關(guān)了嗎?前端基礎(chǔ)架構(gòu)和硬核介紹 showImg(https://segmentfault.com/img/remote/...
摘要:前端準(zhǔn)備前端了解過關(guān)了嗎前端基礎(chǔ)架構(gòu)和硬核介紹技術(shù)棧的選擇首先我們構(gòu)建前端架構(gòu)需要對前端生態(tài)圈有一切了解,并且最好帶有一定的技術(shù)前瞻性,好的技術(shù)架構(gòu)可能日后會方便的擴展,減少重構(gòu)的次數(shù),即使重構(gòu)也不需要大動干戈,我通常選型技術(shù)棧會參考以下三 # 前端準(zhǔn)備 :前端了解過關(guān)了嗎?前端基礎(chǔ)架構(gòu)和硬核介紹 showImg(https://segmentfault.com/img/remote/...
閱讀 3574·2023-04-26 02:05
閱讀 2016·2021-11-19 11:30
閱讀 4225·2021-09-30 09:59
閱讀 3182·2021-09-10 10:51
閱讀 2612·2021-09-01 10:30
閱讀 1491·2021-08-11 11:20
閱讀 2621·2019-08-30 15:54
閱讀 571·2019-08-30 10:49