來源於:阿賢博客javascript
下一代web開發框架
npm install easywebpack-cli -g
npm install
npm run dev
npm run build
npm start
├── app │ ├── controller //控制器(模式:C層) │ │ ├── test │ │ └── test.js │ ├── models //數據模型(模式:M層) │ │ ├── api //接口api │ │ ├── mocks //數據處理 │ │ ├── app │ │ └── home │ ├── extend │ ├── lib │ ├── middleware │ ├── mocks │ ├── proxy │ ├── router.js //服務器端路由 │ ├── view //視圖(模式:V層,工具生成) │ │ ├── about // 服務器編譯的jsbundle文件 │ │ │ └── about.js │ │ ├── home │ │ │ └── home.js // 服務器編譯的jsbundle文件 │ │ └── layout // 用於根據指定的layout生成對應的html頁面, 用於服務器渲染失敗時,採用客戶端渲染 │ │ └── layout.html │ └── web //視圖(前端工程目錄開發-->生成模式:V層) │ ├── asset // 存放公共js,css資源 │ ├── framework // 前端公共庫和第三方庫 │ │ ├── fastclick │ │ │ └── fastclick.js │ │ ├── sdk │ │ │ ├── sdk.js │ │ ├── storage │ │ │ └── storage.js │ │ └── vue // 與vue相關的公開代碼 │ │ ├── app.js // 先後端調用入口, 默認引入componet/directive/filter │ │ ├── component.js // 組件入口, 能夠增長component目錄,相似下面的directive │ │ ├── directive // directive 目錄,存放各類directive組件 │ │ ├── directive.js // directive引用入口 │ │ └── filter.js // filter引用入口 │ ├── page // 前端頁面和webpack構建目錄, 也就是webpack打包配置entryDir │ │ ├── home // 每一個頁面遵循目錄名, js文件名, scss文件名, vue文件名相同 │ │ │ ├── home.scss │ │ │ ├── home.vue │ │ │ ├── images // 頁面自有圖片,公共圖片和css放到asset下面 │ │ │ │ └── icon_more.png │ │ │ └── w-week // 頁面自有組件,公共組件放到widget下面 │ │ │ ├── w-week.scss │ │ │ └── w-week.vue │ │ └── test // 每一個頁面遵循目錄名, js文件名, scss文件名, vue文件名相同 │ │ └── test.vue │ ├── store // 引入vuex 的基本規範, 能夠分模塊 │ │ ├── app │ │ │ ├── actions.js │ │ │ ├── getters.js │ │ │ ├── index.js │ │ │ ├── mutation-type.js │ │ │ └── mutations.js │ │ └── store.js │ └── component // 公共業務組件, 好比loading, toast等, 遵循目錄名, js文件名, scss文件名, vue文件名相同 │ ├── loading │ │ ├── loading.scss │ │ └── loading.vue │ ├── test │ │ ├── test.vue │ │ └── test.scss │ └── toast │ ├── toast.scss │ └── toast.vue ├── build // webpack 自定義配置入口, 會與默認配置進行合併(看似這麼多,其實這裏只是佔個位說明一下) │ ├── base │ │ └── index.js // 公共配置 │ ├── client // 客戶端webpack編譯配置 │ │ ├── dev.js │ │ ├── prod.js │ │ └── index.js │ ├── server // 服務端webpack編譯配置 │ │ ├── dev.js │ │ ├── prod.js │ │ └── index.js │ └── index.js ├── config │ ├── config.default.js │ ├── config.local.js │ ├── config.prod.js │ ├── config.test.js │ └── plugin.js ├── doc ├── index.js ├── public // webpack編譯目錄結構, render文件查找目錄 │ ├── manifest.json // 資源依賴表 │ ├── static │ │ ├── css │ │ │ ├── home │ │ │ │ ├── home.07012d33.css │ │ │ └── test │ │ │ ├── test.4bbb32ce.css │ │ ├── img │ │ │ ├── change_top.4735c57.png │ │ │ └── intro.0e66266.png │ ├── test │ │ └── test.js │ └── vendor.js // 生成的公共打包庫
/*文件目錄:project/app/router.js*/ module.exports = app => { app.get('/', app.controller.home.home.index); app.get('/app/api/article/list', app.controller.app.app.list); app.get('/app/api/article/:id', app.controller.app.app.detail); app.get('/app(/.+)?', app.controller.app.app.index); };
/*文件目錄:project/app/controller/home/home.js*/ const Controller = require('egg').Controller; const Mocks = require('../../models/mocks/home/init'); class HomeController extends Controller { mocks(){ const { ctx } = this; return new Mocks({ctx: ctx}); } async index() {//頁面 const { ctx } = this; const mocks = this.mocks(); await ctx.render('index/index.js', { title: 'egg vue ssr', list: await mocks.index() //發接口獲取的數據 }); } async pager() {//接口 const { ctx } = this; const mocks = this.mocks(); const pageIndex = ctx.query.pageIndex; const pageSize = ctx.query.pageSize; ctx.body = await mocks.index();//發接口獲取的數據 } }; module.exports = HomeController;
//獲取(46),好比:http://localhost:7001/app/detail/46 const id = this.ctx.params.id;
//獲取(20),好比:http://localhost:7001/?page=20 const page = this.ctx.query.page;
//獲取(20),好比:http://localhost:7001/?page=20 const protocol = this.ctx.protocol;
await ctx.render('app/app.js', {});
this.ctx.body = {title: '接口'};
瞭解更多php
/* 文件目錄:project/app/models/api/api.js */ const axios = require('axios'); const Config = require('../../config/config'); class Api { constructor(opts) { console.log(88777); } fetch(_opt) { var param = '', opt = Object.assign({ baseHost: Config.apiHost, protocol: Config.apiProtocol, urlMap: '', url: '', method: 'GET', type: 'json', cookies: true, //Boolean timeout: 10000, param: null, //{id: 123} paramType: 0 // 0表示參數以字符串形式提交好比「wen=12&xx=333」; 1表示參數以對象形式提交好比「{wen:12, xx:33}」 }, _opt); //考慮redis緩存處理 if (opt.urlMap !== '') { opt.url = `${opt.baseHost}${opt.urlMap}`; } if (opt.protocol === 'https') { opt.url = (opt.url).replace(/^http:(\/\/[\w])/, 'https:$1'); } console.log('opt.url', opt.url); if (opt.param !== null) { for (var key in opt.param) { if (typeof opt.param[key] !== 'function') { param += '&'+key+'='+ encodeURIComponent(opt.param[key]); } } param = param.substring(1); } return axios(opt.url, { method: opt.method, timeout: opt.timeout, data: (opt.paramType === 0) ? param : opt.param, withCredentials: opt.cookies }).then(function(response) { return response.data; }).catch(function(ex) { return { error: true, url: ex }; }); } } //export default Api; module.exports = new Api();
/*目錄文件:project/app/models/mocks/app/init.js*/ const { fetch } = require('../../api/api'); class Mocks { constructor(suppor) { this.ctx = suppor.ctx; } index(){ let urlMap = '/menus', protocol = this.ctx.protocol; return fetch({ url: 'http://m.aipai.com/mobile/apps/apps.php?module=gameIndex&func=newAsset&sort=click&appId=11616&page=3&pageSize=12', //urlMap: urlMap, protocol: protocol,//ctx.protocol, method: 'get', type: "jsonp", //param: param }).then(ret=>{ //訪問超時or資源地址出錯 if(typeof ret.error !== 'undefined' && ret.error){ //msg = '網絡錯誤'; }else{ } return ret; }); } }; module.exports = Mocks;
//使用計算屬性 computed: { title(){ return this.serverData.title; }, lists(){ return this.serverData.list; } },
/*目錄文件:project/app/web/page/app/views/index.vue*/ preFetch ({ state, dispatch, commit }) {//只在服務器端執行; preFetch比created執行快 return Promise.all([ dispatch('FETCH_ARTICLE_LIST_PRE') ]) }, beforeMount() {//只在客戶端執行; created比beforeMount執行快 let serverData = this.$store.state.serverData; if(serverData.articleList && serverData.articleList.length >0){ this.$store.commit('SET_ARTICLE_LIST', serverData.articleList); }else{ return Promise.all([ this.$store.dispatch('FETCH_ARTICLE_LIST') ]); } }
<layout :layoutData="layoutData"> <router-view></router-view> <!-- <transition name="fade" mode="out-in"> </transition> --> </layout>
/*目錄文件:project/app/web/page/app/app.vue*/ computed: { layoutData() { let state = this.$store.state; return { title: state.serverData.title+": 2018世界盃大數據報告", keywords: "keywords", description: "description", pluginCss: ['//www.xxx.com/static/index.min.css'], pluginJs: ["//www.xxx.com/static/libs/zepto.min.1.2.0.js"], pluginFooterJs: ["//www.xxx.com/static/libs/zepto.min.1.2.0.js"], }; } },
備註: 服務端與客戶端數據經過渲染window.__INITIAL_STATE__來橋接的
來源於:阿賢博客css