├── .gitignore ├── config ├── utils.js └── command.js ├── template ├── entry.ejs ├── routerT │ ├── childs │ │ ├── child2.vue │ │ └── child1.vue │ ├── entry.ejs │ ├── html.ejs │ └── component.ejs ├── html.ejs └── component.ejs ├── command ├── list.js ├── init.js ├── delete.js └── add.js ├── package.json ├── README.md └── bin └── vue-app /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ -------------------------------------------------------------------------------- /config/utils.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | toHump: str => str.replace(/-(\w)/g, 3 | x => x.slice(1).toUpperCase()), 4 | toLine: str => str.replace(/([A-Z])/g,"-$1").toLowerCase() 5 | } -------------------------------------------------------------------------------- /config/command.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | init: 'init', 3 | add: 'add', 4 | delete: 'delete', 5 | list: 'list', 6 | i: 'i', 7 | a: 'a', 8 | d: 'd', 9 | l: 'l' 10 | } -------------------------------------------------------------------------------- /template/entry.ejs: -------------------------------------------------------------------------------- 1 | import <%= pageName %> from "@/components/<%= pageName %>/index" 2 | //使用阿里flexible解决方案 3 | import 'amfe-flexible' 4 | 5 | new Vue({ 6 | el: '#app', 7 | components: { <%= pageName %> } 8 | }); 9 | -------------------------------------------------------------------------------- /template/routerT/childs/child2.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /template/routerT/childs/child1.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 16 | -------------------------------------------------------------------------------- /command/list.js: -------------------------------------------------------------------------------- 1 | const exec = require('child_process').exec; 2 | const co = require('co'); 3 | const fs = require('fs'); 4 | const chalk = require('chalk'); 5 | 6 | module.exports = () => { 7 | co(function* () { 8 | fs.readdir('./pages', (err, data) => { 9 | if(err) { 10 | console.log(chalk.red('\n Can not find pages, please make sure that you are in the root of the project')) 11 | process.exit(); 12 | } 13 | console.log(data); 14 | process.exit(); 15 | }) 16 | }) 17 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "bin": { 3 | "vue-app": "bin/vue-app" 4 | }, 5 | "name": "vue-app-cli", 6 | "version": "1.4.0", 7 | "description": "优化devool", 8 | "main": "index.js", 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "chalk": "^2.3.1", 16 | "co": "^4.6.0", 17 | "co-prompt": "^1.0.0", 18 | "commander": "^2.14.1", 19 | "download-git-repo": "^1.0.2", 20 | "ejs": "^2.5.7", 21 | "ora": "^2.0.0", 22 | "rimraf": "^2.6.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /template/routerT/entry.ejs: -------------------------------------------------------------------------------- 1 | import <%= pageName %> from "@/components/<%= pageName %>/index" 2 | import Child1 from '@/components/<%= pageName %>/childs/child1' 3 | import Child2 from '@/components/<%= pageName %>/childs/child2' 4 | import VueRouter from 'vue-router' 5 | //使用阿里flexible解决方案 6 | import 'amfe-flexible' 7 | 8 | Vue.use(VueRouter) 9 | 10 | const routes = [{ 11 | path: '/child1', 12 | component: Child1 13 | }, { 14 | path: '/child2', 15 | component: Child2 16 | }] 17 | 18 | const router = new VueRouter({ 19 | routes 20 | }) 21 | 22 | new Vue({ 23 | el: '#app', 24 | router, 25 | components: { <%= pageName %> } 26 | }); 27 | -------------------------------------------------------------------------------- /template/html.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= pageName %> 7 | 8 | 9 | 10 | 11 | 12 |
13 | <<%= labelName %>>> 14 |
15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /template/component.ejs: -------------------------------------------------------------------------------- 1 | 9 | 10 | 20 | 21 | -------------------------------------------------------------------------------- /template/routerT/html.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= pageName %> 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | <<%= labelName %>>> 15 |
16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /template/routerT/component.ejs: -------------------------------------------------------------------------------- 1 | 12 | 13 | 23 | 24 | -------------------------------------------------------------------------------- /command/init.js: -------------------------------------------------------------------------------- 1 | const co = require('co'); 2 | const chalk = require('chalk'); 3 | const download = require('download-git-repo'); 4 | const ora = require('ora'); 5 | const exists = require('fs').existsSync; 6 | const rm = require('rimraf').sync; 7 | 8 | module.exports = (projectName) => { 9 | co(function*() { 10 | console.log(chalk.white('\n Start generating...')); 11 | const spinner = ora('Start downloading templates...').start(); 12 | if (exists(projectName)) rm(projectName); 13 | download('1335382915/vue-app-template', projectName, (err) => { 14 | if(err) { 15 | console.log(chalk.red(err)); 16 | process.exit(); 17 | } 18 | spinner.stop(); 19 | console.log(chalk.green('\n Download succeed! Enter your project and use `npm install`')); 20 | process.exit(); 21 | }) 22 | }) 23 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://upload-images.jianshu.io/upload_images/1495096-1d5b56057c3a3840.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 2 | 3 | ![](https://upload-images.jianshu.io/upload_images/1495096-4bd9c292e9f8fec8.gif?imageMogr2/auto-orient/strip) 4 | 5 | ### 一个能够让你快速构建基于`vue`的`app`多页应用脚手架,可独立开发`h5 app`或集成到`hbuilder`开发原生应用。 6 | 7 | ### [文档地址](https://mescalchuan.github.io/vue-app-cli) 8 | 9 | #### vue-cli 10 | 之所以不使用官方的`vue-cli`构建应用是因为: 11 | * `vue-cli`需要自己实现多页面构建。 12 | * 在开发环境(`npm start`)下会创建服务器,一切构建结果都存放在内存中,本地无法访问,导致`app`变为空白页。 13 | * 只有在生产环境(`npm run build`)下才会构建到本地,却失去了对模块的实时监控。 14 | * `webpack`拆分太细,功能太全,很多功能在`app`端都不会用到。 15 | 16 | 以上不足均可以自行修改`vue-cli`实现构建最优化,出于时间成本的考虑,最终决定在已有的`angular-m-cli`的基础上完成适合`app`开发的脚手架构建。 17 | 18 | #### vue-app-cli 19 | 可以快速构建基于vue的app多页应用,对`h5页面app`和`dcloud原生app`都十分友好。 20 | 21 | 它实现了以下功能: 22 | * 快速生成`app`模板 23 | * 快速创建新页面 24 | * 支持`es6` 25 | * 支持`.vue`文件 26 | * 基于`sass`编写样式文件 27 | * 模块导入样式文件 28 | * 错误映射 29 | 30 | ### 参考 31 | [vue-cli](https://github.com/vuejs/vue-cli)、[教你从零开始搭建一款前端脚手架工具](https://segmentfault.com/a/1190000006190814) 32 | 33 | -------------------------------------------------------------------------------- /bin/vue-app: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict' 3 | process.env.NODE_PATH = __dirname + '/../node_modules/'; 4 | 5 | const program = require('commander'); 6 | const commandConfig = require('../config/command'); 7 | const add = require('../command/add'); 8 | const list = require('../command/list'); 9 | const _delete = require('../command/delete'); 10 | const init = require('../command/init'); 11 | 12 | program.version('1.4.0', '-v', '--version'); 13 | 14 | program.usage(''); 15 | 16 | program 17 | .command('init ') 18 | .description('Create a new project') 19 | .alias('i') 20 | .action(projectName => { 21 | init(projectName) 22 | }); 23 | 24 | program 25 | .command('add ') 26 | .option('-r, --vuerouter', 'use vue-router page') 27 | .description('Add a new page, use -vr to add a vue-router-page') 28 | .alias('a') 29 | .action((pageName, cmd) => { 30 | add(pageName, cmd.vuerouter ? 'vr' : '') 31 | }); 32 | 33 | program 34 | .command('list') 35 | .description('List all the pages') 36 | .alias('l') 37 | .action(() => { 38 | list() 39 | }); 40 | 41 | program 42 | .command('delete ') 43 | .description('Delete a page') 44 | .alias('d') 45 | .action(pageName => { 46 | _delete(pageName) 47 | }); 48 | 49 | program.parse(process.argv) 50 | 51 | if(!program.args.length){ 52 | program.help() 53 | } 54 | -------------------------------------------------------------------------------- /command/delete.js: -------------------------------------------------------------------------------- 1 | const co = require('co'); 2 | const fs = require('fs'); 3 | const chalk = require('chalk'); 4 | const ora = require('ora'); 5 | const rm = require('rimraf').sync; 6 | const prompt = require('co-prompt'); 7 | 8 | module.exports = (pageName) => { 9 | co(function* () { 10 | const containerName = pageName; 11 | 12 | console.log(chalk.yellow(`\n Your './entry/${containerName}.js, './pages/${containerName}.html', './components/${containerName}' will be removed.`)); 13 | const answer = yield prompt('\n are you sure? Input y or n \n'); 14 | 15 | if(answer == 'n' || !answer) process.exit(); 16 | 17 | const destinationEntry = `./entry/${containerName}.js`; 18 | const destinationPage = `./pages/${containerName}.html`; 19 | const destinationComponent = `./components/${containerName}`; 20 | 21 | try { 22 | fs.readFileSync(destinationEntry); 23 | fs.readFileSync(destinationPage); 24 | fs.readdirSync(destinationComponent); 25 | } 26 | catch(err) { 27 | console.log(chalk.red(`\n Can not find ${containerName}.`)); 28 | process.exit(); 29 | } 30 | 31 | const spinner = ora(`Deleting ${containerName} pages...`).start(); 32 | try { 33 | rm(destinationEntry); 34 | rm(destinationPage); 35 | rm(destinationComponent); 36 | } 37 | catch(err) { 38 | spinner.stop(); 39 | console.log(chalk.red(`\n Can not delete ${containerName}`)); 40 | process.exit(); 41 | } 42 | spinner.stop(); 43 | console.log(chalk.green('\n Delete succeed!')); 44 | process.exit(); 45 | }) 46 | } -------------------------------------------------------------------------------- /command/add.js: -------------------------------------------------------------------------------- 1 | const co = require('co') 2 | const ejs = require('ejs'); 3 | const fs = require('fs'); 4 | const chalk = require('chalk') 5 | const path = require('path'); 6 | const ora = require('ora'); 7 | const utils = require('../config/utils'); 8 | 9 | module.exports = (pageName, vr) => { 10 | co(function*() { 11 | //读取模板文件 12 | const vrPath = vr ? 'routerT/' : '' 13 | const entryEJS = fs.readFileSync(path.resolve(__dirname, `../template/${vrPath}entry.ejs`), 'utf-8'); 14 | const htmlEJS = fs.readFileSync(path.resolve(__dirname, `../template/${vrPath}html.ejs`), 'utf-8'); 15 | const componentEJS = fs.readFileSync(path.resolve(__dirname, `../template/${vrPath}component.ejs`), 'utf-8'); 16 | 17 | //参数获取新建container名字并转换成驼峰 18 | const containerName = utils.toHump(pageName); 19 | const labelName = utils.toLine(containerName); 20 | //文件路径 21 | const destinationEntryJS = `./entry/${containerName}.js`; 22 | const destinationPageHTML = `./pages/${containerName}.html`; 23 | const destinationComponent = `./components/${containerName}`; 24 | const destinationComponentVue = `./components/${containerName}/index.vue`; 25 | 26 | //渲染模板文件 27 | const entryResult = ejs.render(entryEJS, {pageName: containerName}); 28 | const htmlResult = ejs.render(htmlEJS, {pageName: containerName, labelName}); 29 | const componentResult = ejs.render(componentEJS, {pageName: containerName}); 30 | 31 | const entryExist = fs.existsSync(destinationEntryJS); 32 | const pageExist = fs.existsSync(destinationPageHTML); 33 | const componentPathExist = fs.existsSync(destinationComponent); 34 | if(entryExist) { 35 | console.log(chalk.red(`\n The project has the same container, see your entry/${containerName}.js`)); 36 | process.exit(); 37 | } 38 | if(pageExist) { 39 | console.log(chalk.red(`\n The project has the same container, see your pages/${containerName}.html`)); 40 | process.exit(); 41 | } 42 | if(componentPathExist) { 43 | console.log(chalk.red(`\n The project has the same container, see your components/${containerName} folder.`)); 44 | process.exit(); 45 | } 46 | 47 | //复制文件 48 | const spinner = ora(`Creating ${containerName} pages...`).start(); 49 | try { 50 | fs.mkdirSync(destinationComponent); 51 | fs.writeFileSync(destinationEntryJS, entryResult); 52 | fs.writeFileSync(destinationPageHTML, htmlResult); 53 | fs.writeFileSync(destinationComponentVue, componentResult); 54 | if(vr) { 55 | fs.mkdirSync(`./components/${containerName}/childs`); 56 | 57 | const destinationRouterChild1 = `./components/${containerName}/childs/child1.vue`; 58 | const destinationRouterChild2 = `./components/${containerName}/childs/child2.vue`; 59 | 60 | const child1Vue = fs.readFileSync(path.resolve(__dirname, '../template/routerT/childs/child1.vue'), 'utf-8'); 61 | const child2Vue = fs.readFileSync(path.resolve(__dirname, '../template/routerT/childs/child2.vue'), 'utf-8'); 62 | 63 | fs.writeFileSync(destinationRouterChild1, child1Vue); 64 | fs.writeFileSync(destinationRouterChild2, child2Vue); 65 | } 66 | } 67 | catch(err) { 68 | spinner.stop(); 69 | console.log(chalk.red('\n Can not create new page.')); 70 | console.log(err); 71 | process.exit(); 72 | } 73 | spinner.stop(); 74 | console.log(chalk.green('\n Create succeed!')); 75 | process.exit(); 76 | }) 77 | } --------------------------------------------------------------------------------