├── .gitignore ├── LICENSE ├── README.md ├── circle.yml ├── deploy-docs.sh ├── docs ├── README.md ├── SUMMARY.md ├── backend.md ├── commands.md ├── e2e.md ├── env.md ├── linter.md ├── pre-processors.md ├── prerender.md ├── proxy.md ├── static.md ├── structure.md └── unit.md ├── index.html ├── meta.js ├── package.json ├── template ├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .postcssrc.js ├── README.md ├── build │ ├── build.js │ ├── check-versions.js │ ├── dev-client.js │ ├── dev-server.js │ ├── utils.js │ ├── vue-loader.conf.js │ ├── webpack.base.conf.js │ ├── webpack.dev.conf.js │ ├── webpack.prod.conf.js │ └── webpack.test.conf.js ├── config │ ├── _README.md │ ├── dev.env.js │ ├── index.js │ ├── prod.env.js │ └── test.env.js ├── index.html ├── package.json ├── src │ ├── App.vue │ ├── assets │ │ └── logo.png │ ├── components │ │ ├── ProgressBar.vue │ │ └── z_README.md │ ├── config │ │ ├── api │ │ │ ├── api.conf.js │ │ │ ├── api.store.js │ │ │ └── baseapi.js │ │ ├── env │ │ │ ├── env.conf.json │ │ │ └── env.export.js │ │ ├── i18n │ │ │ └── index.js │ │ ├── routes │ │ │ ├── index.js │ │ │ └── routes.store.js │ │ └── z_README.md │ ├── directives │ │ ├── index.js │ │ └── z_README.md │ ├── fetch │ │ ├── demo-api.js │ │ └── z_README.md │ ├── filters │ │ ├── index.js │ │ └── z_README.md │ ├── languages │ │ ├── en.js │ │ ├── z_README.md │ │ └── zh.js │ ├── main.js │ ├── mixins │ │ └── index.js │ ├── pages │ │ ├── demo │ │ │ ├── index.vue │ │ │ └── z-routes.js │ │ └── z_README.md │ └── store │ │ ├── eventbus.js │ │ ├── index.js │ │ ├── modules │ │ ├── demo.js │ │ └── socket_io_demo.js │ │ ├── mutation-types.js │ │ └── z_README.md ├── static │ └── .gitkeep └── test │ ├── e2e │ ├── custom-assertions │ │ └── elementCount.js │ ├── nightwatch.conf.js │ ├── runner.js │ └── specs │ │ └── test.js │ └── unit │ ├── .eslintrc │ ├── index.js │ ├── karma.conf.js │ └── specs │ └── Hello.spec.js └── test.sh /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | docs/_book 4 | test -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 halower 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## vbp 帮你自动搭建一个简单易用的前端业务框架。 2 | ``` 3 | 初始化项目的时候除了soket暂时不需要,其他建议都选择y 4 | 5 | cd mypro 将命令行路径转到项目内 也可以关了到项目内再打开 6 | npm install 此命令将安装项目packa.json中记录的node_modules依赖包 7 | 8 | npm run dev 开发环境热加载项目 会自动打开网页,并在修改代码保存之后,更新网页 9 | npm run build 上线环境打包命令,会生成dist文件夹,内部包含src目录下所有的文件 10 | 注意建议放在服务器根目录不然的话需要修改配置才能正常(原因为,打包默认为绝对路径)访问 11 | 文件/文件夹介绍 12 | index.html 项目html文件,只有这一个html 13 | build、config、test webpack配置以及测试文件夹(一般不需要关心,需要修改webpack配置可以重点关注一下) 14 | static 不需要打包编译的静态文件(npm run build之后会直接放到dist/static里面) 15 | src 源码文件夹 16 | main.js 项目入口,所有文件都是从这里开始 17 | app.vue app入口,默认有一个作为路由的容器 18 | assets 静态资源文件夹(可以存放图片、静态css,js,视频等等),会通过webpack进行打包 19 | 所以使用这里面的文件的时候请使用 import或者require的方式进行引入 20 | components 公共组件文件夹,项目用到的公共模块的存放地方(比如下拉框、table等等)如果引用第三方库,根据需要自行放置 21 | config 项目配置文件夹,包括api,route,多语言的配置 22 | api 项目api的配置文件夹 23 | api.conf.js 项目的后台接口配置文件。 24 | api.store,js 项目api的集中的地方,如果新增了自己的与后台交互的模块,需要在这里引入 25 | baseapi.js 对官方的axios进行二次封装的地方 26 | env 后台服务器地址配置 27 | env.conf,json 后台地址记录json 28 | env.export.js 后台地址分发的js 29 | i18n 多语言配置(大多项目暂时不需要配置多语言) 30 | routes 项目路由配置 31 | index.js 路由配置初始化的地方, 32 | 其中 mode: 'history' ,是前台的路由模式,history会去掉浏览器url中的#,这个需要服务器将404重定向到index.html中 33 | scrollBehavior是路由变化是的统一行为(目前是自动回到页面顶部) 34 | routes.store.js 将其他路由统一引入的文件 35 | directives vue自定义指令文件夹 36 | fetch 具体的逻辑处理文件夹,比如与后台交互需要进行数据处理等等 37 | filters vue自定义过滤器文件夹 38 | languages 多语言具体文件存放 39 | mixins 混入文件夹(不用管) 40 | pages 具体页面文件夹(建议是每一个页面自己相应的配置z-routers.js,如果配置了,需要引入到routes.store.js中) 41 | store vuex(项目状态存放文件夹),通常用来存放项目中公共的状态,比如用户名,等等,其他的也可以分别加入 42 | 需要注意的是,store的获取可以使用getter 和state 但是修改只能使用mutation,否则vue不会动态监听 43 | 44 | ``` 45 | 46 | #### QQ讨论群:`255965810` 47 | # 如何使用 48 | ``` 49 | $ npm install -g vue-cli 50 | $ vue init halower/vbp my-project 51 | $ cd my-project 52 | $ npm install 53 | $ npm run dev 54 | ``` 55 | 56 | # 项目说明 57 | 58 | ## 组件相关文件夹说明 (components) 59 | > 该文件夹建议只写组件 60 | 61 | `怎么判断是组件?` 62 | >是不是可以复用,原则上可以复用的需要抽离成组件,不能复用无需组件化. 63 | 64 | 65 | ## 配置相关文件夹说明 (config) 66 | 67 | ### `api.conf.js` 68 | - 功能: 69 | > 集中管理各个业务模块的后端请求地址,便于排错和集中管理 70 | - 用法: 71 | > 每个模块都导出一个本模块的地址对象即可 72 | ``` 73 | export const demo(模块名称) = { 74 | test: '/api/test' //只需要写相对路径路径 75 | } 76 | ``` 77 | 78 | ### `service.conf.js` 79 | - 功能: 按照项目的需求初始化一些服务的配置参数,例如请求`http://127.0.0.1/api/test`,则根路径(`baseUrl`)为`http://127.0.0.1`,这样在`api.conf.js`只需要写相对路径,其中```baseUrl```为必须参数 80 | > 81 | 82 | ### `api.store.js` 83 | - 功能: 84 | >   作为所有业务请求的入口,将所有的http相关的处理从页面组件中剥离,在服务模块中集中处理完复杂逻辑等操作,减少页面臃肿程度. 85 | - 页面调用方法:(我们将所有的服务模块全部包装到this.$api中,以便识别服务) 86 | ``` 87 | this.$api.user.adduser({name:'halower'}) 88 | ``` 89 | - 依赖文件: 90 | > `api.conf.js`、 `fetch(文件夹)` 91 | 92 | ### `baseapi.js` 93 | - 功能: 94 | > 作为所有服务对象的基类,包含了请求地址代理,统一的请求入口(`get,post` ect.) 95 | 96 | ### `routes.store.js` 97 | - 功能: 98 | > 集中的路由管理中心,将各个业务模块中的路由统一整理合并 99 | - 用法: 100 | 1. 导入每个业务模块的路由单元 101 | ``` 102 | import demo from '@/pages/demo/z-routes 103 | ``` 104 | 2. 添加业务路由 105 | ``` 106 | routes = routes.concat(demo) 107 | ``` 108 | 109 | ## 指令相关文件夹说明 (directives) 110 | 111 | 功能 112 | 全局指令处理,只需要在此处使用Vue.directive即可扩展项目的指令集 113 | 114 | ## 业务服务相关文件夹说明 (fetch) 115 | - 功能 116 | > 将繁琐的数据处理和关联性的逻辑操作从页面中剥离,降低服务与页面的耦合 117 | - 用法 118 | 1. 引入服务基类并继承它 119 | 2. 方法中的url路径需要使用到`/config/api.conf.js` 的模块,由于已经使用代理处理过,因此只需要使用`this.业务模块名.请求地址`的模式即可以获取真实请求路径 120 | ``` 121 | import BaseApiController from '@/config/baseapi' 122 | 123 | /** 124 | * @class 演示业务功能 125 | */ 126 | class DemoApiController extends BaseApiController { 127 | /* 128 | * @method 这里我们只做演示 129 | */ 130 | test () { 131 | // 假设这里有很复杂的前端逻辑 132 | return this.get([this.demo.test], {}).then(res => { 133 | return res.data 134 | }) 135 | } 136 | } 137 | export default new DemoApiController() 138 | ``` 139 | - 依赖文件: 140 | > `api.conf.js` 141 | 142 | ## 业务页面相关文件夹 (pages) 143 | - 功能 144 | > 编写各个业务模块的页面,每个页面都可以独立配置自己的自己独立的业务路由 145 | - 约定 146 | > 147 | 1. 建议路由按照业务模块层级命名,可以有效地避免非规则的命名给我们造成的路由冲突的困扰 148 | 2. 每个业务模块下都使用z-routes.js命名路由的配置文件,这样做仅仅是统一的将路由文件至于 该文件夹的最底下,当然你可以不这么做。 149 | - 用法 150 | > 在配置完路由后,请按照config中的说明文档,将路由导入 routestore.js 151 | 152 | ## 多语言相关文件夹 (languages) 153 | 建议按照不同的业务需要添加分组翻译,便于整理,添加不同的语言类型后在 ```config/i18n.js```引入相应的语言包 154 | 155 | ## 状态相关管理文件夹 (store) 156 | - 功能 157 | > 统一管理项目状态,在复杂的交互项目中可以启用该模块,可以减少事件的不便性 158 | 159 | - 使用方法 160 | > 161 | 1. `mutation-types.js` 在这里进行mutation命名的统一管理 162 | ``` 163 | export const CHANGE_NAME = 'CHANGE_NAME' // 测试更改模块名称 164 | ``` 165 | 在各个模块中可以使用如下方式引用,使用 ` [types.CHANGE_NAME]` 方式获取 166 | 167 | ``` 168 | import * as types from '@/store/mutation-types' 169 | ``` 170 | 171 | 2. 在`modules`文件夹中编写对应的模块,可以引用`fetch`文件夹中的服务在actions中处理业务 172 | 3. 写完模块状态管理后,在`index.js`中引入相应模块 173 | ``` 174 | modules: { 175 | test 176 | } 177 | ``` 178 | 4. 页面使用 179 | 180 | ``` 181 | 188 | 189 | 204 | 205 | ``` 206 | 5. 说明 207 | - 模块中需要使用 `namespaced: true` 标记 208 | - 根节点状态可以直接在 `index.js` 编写 209 | - 能不用this.$store 最好不要使用,保持页面整洁 210 | 211 | 6. `eventbus.js` 主要用于简单的非父子组件交互通信使用 212 | > 使用方法 213 | ``` 214 | this.$eventbus.on(event: string | Array, fn: Function) 215 | this.$eventbus.emit(event: string, data: Object) 216 | ``` 217 | #### 目前正在使用的企业 218 | 219 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | node: 3 | version: stable 4 | 5 | dependencies: 6 | pre: 7 | - sudo sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list' 8 | - sudo apt-get update 9 | - sudo apt-get install google-chrome-stable 10 | 11 | test: 12 | override: 13 | - bash test.sh 14 | -------------------------------------------------------------------------------- /deploy-docs.sh: -------------------------------------------------------------------------------- 1 | cd docs 2 | rm -rf _book 3 | gitbook build 4 | cd _book 5 | git init 6 | git add -A 7 | git commit -m 'update book' 8 | git push -f git@github.com:vuejs-templates/webpack.git master:gh-pages 9 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | This boilerplate is targeted towards large, serious projects and assumes you are somewhat familiar with Webpack and `vue-loader`. Make sure to also read [`vue-loader`'s documentation](https://vue-loader.vuejs.org/) for common workflow recipes. 4 | 5 | If you just want to try out `vue-loader` or whip out a quick prototype, use the [webpack-simple](https://github.com/vuejs-templates/webpack-simple) template instead. 6 | 7 | ## Quickstart 8 | 9 | To use this template, scaffold a project with [vue-cli](https://github.com/vuejs/vue-cli). **It is recommended to use npm 3+ for a more efficient dependency tree.** 10 | 11 | ``` bash 12 | $ npm install -g vue-cli 13 | $ vue init webpack my-project 14 | $ cd my-project 15 | $ npm install 16 | $ npm run dev 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Project Structure](structure.md) 4 | - [Build Commands](commands.md) 5 | - [Linter Configuration](linter.md) 6 | - [Pre-Processors](pre-processors.md) 7 | - [Handling Static Assets](static.md) 8 | - [Environment Variables](env.md) 9 | - [Integrate with Backend Framework](backend.md) 10 | - [API Proxying During Development](proxy.md) 11 | - [Unit Testing](unit.md) 12 | - [End-to-end Testing](e2e.md) 13 | - [Prerendering for SEO](prerender.md) 14 | -------------------------------------------------------------------------------- /docs/backend.md: -------------------------------------------------------------------------------- 1 | # Integrating with Backend Framework 2 | 3 | If you are building a purely-static app (one that is deployed separately from the backend API), then you probably don't even need to edit `config/index.js`. However, if you want to integrate this template with an existing backend framework, e.g. Rails/Django/Laravel, which comes with their own project structures, you can edit `config/index.js` to directly generate front-end assets into your backend project. 4 | 5 | Let's take a look at the default `config/index.js`: 6 | 7 | ``` js 8 | // config/index.js 9 | 'use strict' 10 | const path = require('path') 11 | 12 | module.exports = { 13 | build: { 14 | index: path.resolve(__dirname, 'dist/index.html'), 15 | assetsRoot: path.resolve(__dirname, 'dist'), 16 | assetsSubDirectory: 'static', 17 | assetsPublicPath: '/', 18 | productionSourceMap: true 19 | }, 20 | dev: { 21 | port: 8080, 22 | proxyTable: {} 23 | } 24 | } 25 | ``` 26 | 27 | Inside the `build` section, we have the following options: 28 | 29 | ### `build.index` 30 | 31 | > Must be an absolute path on your local file system. 32 | 33 | This is where the `index.html` (with injected asset URLs) will be generated. 34 | 35 | If you are using this template with a backend-framework, you can edit `index.html` accordingly and point this path to a view file rendered by your backend app, e.g. `app/views/layouts/application.html.erb` for a Rails app, or `resources/views/index.blade.php` for a Laravel app. 36 | 37 | ### `build.assetsRoot` 38 | 39 | > Must be an absolute path on your local file system. 40 | 41 | This should point to the root directory that contains all the static assets for your app. For example, `public/` for both Rails/Laravel. 42 | 43 | ### `build.assetsSubDirectory` 44 | 45 | Nest webpack-generated assets under this directory in `build.assetsRoot`, so that they are not mixed with other files you may have in `build.assetsRoot`. For example, if `build.assetsRoot` is `/path/to/dist`, and `build.assetsSubDirectory` is `static`, then all Webpack assets will be generated in `path/to/dist/static`. 46 | 47 | This directory will be cleaned before each build, so it should only contain assets generated by the build. 48 | 49 | Files inside `static/` will be copied into this directory as-is during build. This means if you change this prefix, all your absolute URLs referencing files in `static/` will also need to be changed. See [Handling Static Assets](static.md) for more details. 50 | 51 | ### `build.assetsPublicPath` 52 | 53 | This should be the URL path where your `build.assetsRoot` will be served from over HTTP. In most cases, this will be root (`/`). Only change this if your backend framework serves static assets with a path prefix. Internally, this is passed to Webpack as `output.publicPath`. 54 | 55 | ### `build.productionSourceMap` 56 | 57 | Whether to generate source maps for production build. 58 | 59 | ### `dev.port` 60 | 61 | Specify the port for the dev server to listen to. 62 | 63 | ### `dev.proxyTable` 64 | 65 | Define proxy rules for the dev server. See [API Proxying During Development](proxy.md) for more details. 66 | -------------------------------------------------------------------------------- /docs/commands.md: -------------------------------------------------------------------------------- 1 | # Build Commands 2 | 3 | All build commands are executed via [NPM Scripts](https://docs.npmjs.com/misc/scripts). 4 | 5 | ### `npm run dev` 6 | 7 | > Starts a Node.js local development server. See [API Proxying During Development](proxy.md) for more details. 8 | 9 | - Webpack + `vue-loader` for single file Vue components. 10 | - State preserving hot-reload 11 | - State preserving compilation error overlay 12 | - Lint-on-save with ESLint 13 | - Source maps 14 | 15 | ### `npm run build` 16 | 17 | > Build assets for production. See [Integrating with Backend Framework](backend.md) for more details. 18 | 19 | - JavaScript minified with [UglifyJS](https://github.com/mishoo/UglifyJS2). 20 | - HTML minified with [html-minifier](https://github.com/kangax/html-minifier). 21 | - CSS across all components extracted into a single file and minified with [cssnano](https://github.com/ben-eb/cssnano). 22 | - All static assets compiled with version hashes for efficient long-term caching, and a production `index.html` is auto-generated with proper URLs to these generated assets. 23 | 24 | ### `npm run unit` 25 | 26 | > Run unit tests in PhantomJS with [Karma](https://karma-runner.github.io/). See [Unit Testing](unit.md) for more details. 27 | 28 | - Supports ES2015+ in test files. 29 | - Supports all webpack loaders. 30 | - Easy [mock injection](http://vuejs.github.io/vue-loader/en/workflow/testing-with-mocks.html). 31 | 32 | ### `npm run e2e` 33 | 34 | > Run end-to-end tests with [Nightwatch](http://nightwatchjs.org/). See [End-to-end Testing](e2e.md) for more details. 35 | 36 | - Run tests in multiple browsers in parallel. 37 | - Works with one command out of the box: 38 | - Selenium and chromedriver dependencies automatically handled. 39 | - Automatically spawns the Selenium server. 40 | -------------------------------------------------------------------------------- /docs/e2e.md: -------------------------------------------------------------------------------- 1 | # End-to-end Testing 2 | 3 | This boilerplate uses [Nightwatch.js](http://nightwatchjs.org) for e2e tests. Nightwatch.js is a highly integrated e2e test runner built on top of Selenium. This boilerplate comes with Selenium server and chromedriver binaries pre-configured for you, so you don't have to mess with these yourself. 4 | 5 | Let's take a look at the files in the `test/e2e` directory: 6 | 7 | - `runner.js` 8 | 9 | A Node.js script that starts the dev server, and then launches Nightwatch to run tests against it. This is the script that will run when you run `npm run e2e`. 10 | 11 | - `nightwatch.conf.js` 12 | 13 | Nightwatch configuration file. See [Nightwatch's docs on configuration](http://nightwatchjs.org/guide#settings-file) for more details. 14 | 15 | - `custom-assertions/` 16 | 17 | Custom assertions that can be used in Nightwatch tests. See [Nightwatch's docs on writing custom assertions](http://nightwatchjs.org/guide#writing-custom-assertions) for more details. 18 | 19 | - `specs/` 20 | 21 | Your actual tests! See [Nightwatch's docs on writing tests](http://nightwatchjs.org/guide#writing-tests) and [API reference](http://nightwatchjs.org/api) for more details. 22 | 23 | ### Running Tests in More Browsers 24 | 25 | To configure which browsers to run the tests in, add an entry under "test_settings" in [`test/e2e/nightwatch.conf.js`](https://github.com/vuejs-templates/webpack/blob/master/template/test/e2e/nightwatch.conf.js#L17-L39) , and also the `--env` flag in [`test/e2e/runner.js`](https://github.com/vuejs-templates/webpack/blob/master/template/test/e2e/runner.js#L15). If you wish to configure remote testing on services like SauceLabs, you can either make the Nightwatch config conditional based on environment variables, or use a separate config file altogether. Consult [Nightwatch's docs on Selenium](http://nightwatchjs.org/guide#selenium-settings) for more details. 26 | -------------------------------------------------------------------------------- /docs/env.md: -------------------------------------------------------------------------------- 1 | # Environment Variables 2 | 3 | Sometimes it is practical to have different config values according to the environment that the application is running in. 4 | 5 | As an example: 6 | 7 | ```js 8 | // config/prod.env.js 9 | module.exports = { 10 | NODE_ENV: '"production"', 11 | DEBUG_MODE: false, 12 | API_KEY: '"..."' // this is shared between all environments 13 | } 14 | 15 | // config/dev.env.js 16 | module.exports = merge(prodEnv, { 17 | NODE_ENV: '"development"', 18 | DEBUG_MODE: true // this overrides the DEBUG_MODE value of prod.env 19 | }) 20 | 21 | // config/test.env.js 22 | module.exports = merge(devEnv, { 23 | NODE_ENV: '"testing"' 24 | }) 25 | ``` 26 | 27 | > **Note:** string variables need to be wrapped into single and double quotes `'"..."'` 28 | 29 | So, the environment variables are: 30 | - Production 31 | - NODE_ENV = 'production', 32 | - DEBUG_MODE = false, 33 | - API_KEY = '...' 34 | - Development 35 | - NODE_ENV = 'development', 36 | - DEBUG_MODE = true, 37 | - API_KEY = '...' 38 | - Testing 39 | - NODE_ENV = 'testing', 40 | - DEBUG_MODE = true, 41 | - API_KEY = '...' 42 | 43 | As we can see, `test.env` inherits the `dev.env` and the `dev.env` inherits the `prod.env`. 44 | 45 | ### Usage 46 | 47 | It is simple to use the environment variables in your code. For example: 48 | 49 | ```js 50 | Vue.config.productionTip = process.env.NODE_ENV === 'production' 51 | ``` 52 | -------------------------------------------------------------------------------- /docs/linter.md: -------------------------------------------------------------------------------- 1 | # Linter Configuration 2 | 3 | This boilerplate uses [ESLint](https://eslint.org/) as the linter, and uses the [Standard](https://github.com/feross/standard/blob/master/RULES.md) preset with some small customizations. 4 | 5 | If you are not happy with the default linting rules, you have several options: 6 | 7 | 1. Overwrite individual rules in `.eslintrc.js`. For example, you can add the following rule to enforce semicolons instead of omitting them: 8 | 9 | ``` js 10 | // .eslintrc.js 11 | "semi": [2, "always"] 12 | ``` 13 | 14 | 2. Pick a different ESLint preset when generating the project, for example [eslint-config-airbnb](https://github.com/airbnb/javascript/tree/master/packages/eslint-config-airbnb). 15 | 16 | 3. Pick "none" for ESLint preset when generating the project and define your own rules. See [ESLint documentation](https://eslint.org/docs/rules/) for more details. 17 | -------------------------------------------------------------------------------- /docs/pre-processors.md: -------------------------------------------------------------------------------- 1 | # Pre-Processors 2 | 3 | This boilerplate has pre-configured CSS extraction for most popular CSS pre-processors including LESS, SASS, Stylus, and PostCSS. To use a pre-processor, all you need to do is installing the appropriate webpack loader for it. For example, to use SASS: 4 | 5 | ``` bash 6 | npm install sass-loader node-sass --save-dev 7 | ``` 8 | 9 | Note you also need to install `node-sass` because `sass-loader` depends on it as a peer dependency. 10 | 11 | ### Using Pre-Processors inside Components 12 | 13 | Once installed, you can use the pre-processors inside your `*.vue` components using the `lang` attribute on ` 19 | ``` 20 | 21 | ### A note on SASS syntax 22 | 23 | - `lang="scss"` corresponds to the CSS-superset syntax (with curly braces and semicolons). 24 | - `lang="sass"` corresponds to the indentation-based syntax. 25 | 26 | ### PostCSS 27 | 28 | Styles in `*.vue` files are piped through PostCSS by default, so you don't need to use a specific loader for it. You can simply add PostCSS plugins you want to use in `build/webpack.base.conf.js` under the `vue` block: 29 | 30 | ``` js 31 | // build/webpack.base.conf.js 32 | module.exports = { 33 | // ... 34 | vue: { 35 | postcss: [/* your plugins */] 36 | } 37 | } 38 | ``` 39 | 40 | See [vue-loader's related documentation](http://vuejs.github.io/vue-loader/en/features/postcss.html) for more details. 41 | 42 | ### Standalone CSS Files 43 | 44 | To ensure consistent extraction and processing, it is recommended to import global, standalone style files from your root `App.vue` component, for example: 45 | 46 | ``` html 47 | 48 | 49 | ``` 50 | 51 | Note you should probably only do this for the styles written by yourself for your application. For existing libraries e.g. Bootstrap or Semantic UI, you can place them inside `/static` and reference them directly in `index.html`. This avoids extra build time and also is better for browser caching. (See [Static Asset Handling](static.md)) 52 | -------------------------------------------------------------------------------- /docs/prerender.md: -------------------------------------------------------------------------------- 1 | # Prerendering for SEO 2 | 3 | If you want to prerender routes that will not significantly change once pushed to production, use this Webpack plugin: [prerender-spa-plugin](https://www.npmjs.com/package/prerender-spa-plugin), which has been tested for use with Vue. For pages that _do_ frequently change, [Prerender.io](https://prerender.io/) and [Netlify](https://www.netlify.com/pricing) both offer plans for regularly re-prerendering your content for search engines. 4 | 5 | ## Using `prerender-spa-plugin` 6 | 7 | 1. Install it as a dev dependency: 8 | 9 | ```bash 10 | npm install --save-dev prerender-spa-plugin 11 | ``` 12 | 13 | 2. Require it in **build/webpack.prod.conf.js**: 14 | 15 | ```js 16 | // This line should go at the top of the file where other 'imports' live in 17 | const PrerenderSpaPlugin = require('prerender-spa-plugin') 18 | ``` 19 | 20 | 3. Configure it in the `plugins` array (also in **build/webpack.prod.conf.js**): 21 | 22 | ```js 23 | new PrerenderSpaPlugin( 24 | // Path to compiled app 25 | path.join(__dirname, '../dist'), 26 | // List of endpoints you wish to prerender 27 | [ '/' ] 28 | ) 29 | ``` 30 | 31 | If you also wanted to prerender `/about` and `/contact`, then that array would be `[ '/', '/about', '/contact' ]`. 32 | 33 | 4. Enable history mode for `vue-router`: 34 | ```js 35 | const router = new VueRouter({ 36 | mode: 'history', 37 | routes: [...] 38 | }) 39 | ``` 40 | -------------------------------------------------------------------------------- /docs/proxy.md: -------------------------------------------------------------------------------- 1 | # API Proxying During Development 2 | 3 | When integrating this boilerplate with an existing backend, a common need is to access the backend API when using the dev server. To achieve that, we can run the dev server and the API backend side-by-side (or remotely), and let the dev server proxy all API requests to the actual backend. 4 | 5 | To configure the proxy rules, edit `dev.proxyTable` option in `config/index.js`. The dev server is using [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) for proxying, so you should refer to its docs for detailed usage. But here's a simple example: 6 | 7 | ``` js 8 | // config/index.js 9 | module.exports = { 10 | // ... 11 | dev: { 12 | proxyTable: { 13 | // proxy all requests starting with /api to jsonplaceholder 14 | '/api': { 15 | target: 'http://jsonplaceholder.typicode.com', 16 | changeOrigin: true, 17 | pathRewrite: { 18 | '^/api': '' 19 | } 20 | } 21 | } 22 | } 23 | } 24 | ``` 25 | 26 | The above example will proxy the request `/api/posts/1` to `http://jsonplaceholder.typicode.com/posts/1`. 27 | 28 | ## URL Matching 29 | 30 | In addition to static urls you can also use glob patterns to match URLs, e.g. `/api/**`. See [Context Matching](https://github.com/chimurai/http-proxy-middleware#context-matching) for more details. In addition, you can provide a `filter` option that can be a custom function to determine whether a request should be proxied: 31 | 32 | ``` js 33 | proxyTable: { 34 | '**': { 35 | target: 'http://jsonplaceholder.typicode.com', 36 | filter: function (pathname, req) { 37 | return pathname.match('^/api') && req.method === 'GET' 38 | } 39 | } 40 | } 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/static.md: -------------------------------------------------------------------------------- 1 | # Handling Static Assets 2 | 3 | You will notice in the project structure we have two directories for static assets: `src/assets` and `static/`. What is the difference between them? 4 | 5 | ### Webpacked Assets 6 | 7 | To answer this question, we first need to understand how Webpack deals with static assets. In `*.vue` components, all your templates and CSS are parsed by `vue-html-loader` and `css-loader` to look for asset URLs. For example, in `` and `background: url(./logo.png)`, `"./logo.png"` is a relative asset path and will be **resolved by Webpack as a module dependency**. 8 | 9 | Because `logo.png` is not JavaScript, when treated as a module dependency, we need to use `url-loader` and `file-loader` to process it. This boilerplate has already configured these loaders for you, so you basically get features such as filename fingerprinting and conditional base64 inlining for free, while being able to use relative/module paths without worrying about deployment. 10 | 11 | Since these assets may be inlined/copied/renamed during build, they are essentially part of your source code. This is why it is recommended to place Webpack-processed static assets inside `/src`, along side other source files. In fact, you don't even have to put them all in `/src/assets`: you can organize them based on the module/component using them. For example, you can put each component in its own directory, with its static assets right next to it. 12 | 13 | ### Asset Resolving Rules 14 | 15 | - **Relative URLs**, e.g. `./assets/logo.png` will be interpreted as a module dependency. They will be replaced with an auto-generated URL based on your Webpack output configuration. 16 | 17 | - **Non-prefixed URLs**, e.g. `assets/logo.png` will be treated the same as the relative URLs and translated into `./assets/logo.png`. 18 | 19 | - **URLs prefixed with `~`** are treated as a module request, similar to `require('some-module/image.png')`. You need to use this prefix if you want to leverage Webpack's module resolving configurations. For example if you have a resolve alias for `assets`, you need to use `` to ensure that alias is respected. 20 | 21 | - **Root-relative URLs**, e.g. `/assets/logo.png` are not processed at all. 22 | 23 | ### Getting Asset Paths in JavaScript 24 | 25 | In order for Webpack to return the correct asset paths, you need to use `require('./relative/path/to/file.jpg')`, which will get processed by `file-loader` and returns the resolved URL. For example: 26 | 27 | ``` js 28 | computed: { 29 | background () { 30 | return require('./bgs/' + this.id + '.jpg') 31 | } 32 | } 33 | ``` 34 | 35 | **Note the above example will include every image under `./bgs/` in the final build.** This is because Webpack cannot guess which of them will be used at runtime, so it includes them all. 36 | 37 | ### "Real" Static Assets 38 | 39 | In comparison, files in `static/` are not processed by Webpack at all: they are directly copied to their final destination as-is, with the same filename. You must reference these files using absolute paths, which is determined by joining `build.assetsPublicPath` and `build.assetsSubDirectory` in `config.js`. 40 | 41 | As an example, with the following default values: 42 | 43 | ``` js 44 | // config/index.js 45 | module.exports = { 46 | // ... 47 | build: { 48 | assetsPublicPath: '/', 49 | assetsSubDirectory: 'static' 50 | } 51 | } 52 | ``` 53 | 54 | Any file placed in `static/` should be referenced using the absolute URL `/static/[filename]`. If you change `assetSubDirectory` to `assets`, then these URLs will need to be changed to `/assets/[filename]`. 55 | 56 | We will learn more about the config file in the section about [backend integration](backend.md). 57 | -------------------------------------------------------------------------------- /docs/structure.md: -------------------------------------------------------------------------------- 1 | # Project Structure 2 | 3 | ``` bash 4 | . 5 | ├── build/ # webpack config files 6 | │ └── ... 7 | ├── config/ 8 | │   ├── index.js # main project config 9 | │ └── ... 10 | ├── src/ 11 | │   ├── main.js # app entry file 12 | │   ├── App.vue # main app component 13 | │   ├── components/ # ui components 14 | │   │   └── ... 15 | │   └── assets/ # module assets (processed by webpack) 16 | │      └── ... 17 | ├── static/ # pure static assets (directly copied) 18 | ├── test/ 19 | │ └── unit/ # unit tests 20 | │ │   ├── specs/ # test spec files 21 | │ │   ├── index.js # test build entry file 22 | │ │   └── karma.conf.js # test runner config file 23 | │ └── e2e/ # e2e tests 24 | │ │   ├── specs/ # test spec files 25 | │ │   ├── custom-assertions/ # custom assertions for e2e tests 26 | │ │   ├── runner.js # test runner script 27 | │ │   └── nightwatch.conf.js # test runner config file 28 | ├── .babelrc # babel config 29 | ├── .postcssrc.js # postcss config 30 | ├── .eslintrc.js # eslint config 31 | ├── .editorconfig # editor config 32 | ├── index.html # index.html template 33 | └── package.json # build scripts and dependencies 34 | ``` 35 | 36 | ### `build/` 37 | 38 | This directory holds the actual configurations for both the development server and the production webpack build. Normally you don't need to touch these files unless you want to customize Webpack loaders, in which case you should probably look at `build/webpack.base.conf.js`. 39 | 40 | ### `config/index.js` 41 | 42 | This is the main configuration file that exposes some of the most common configuration options for the build setup. See [API Proxying During Development](proxy.md) and [Integrating with Backend Framework](backend.md) for more details. 43 | 44 | ### `src/` 45 | 46 | This is where most of your application code will live in. How to structure everything inside this directory is largely up to you; if you are using Vuex, you can consult the [recommendations for Vuex applications](http://vuex.vuejs.org/en/structure.html). 47 | 48 | ### `static/` 49 | 50 | This directory is an escape hatch for static assets that you do not want to process with Webpack. They will be directly copied into the same directory where webpack-built assets are generated. 51 | 52 | See [Handling Static Assets](static.md) for more details. 53 | 54 | ### `test/unit` 55 | 56 | Contains unit test related files. See [Unit Testing](unit.md) for more details. 57 | 58 | ### `test/e2e` 59 | 60 | Contains e2e test related files. See [End-to-end Testing](e2e.md) for more details. 61 | 62 | ### `index.html` 63 | 64 | This is the **template** `index.html` for our single page application. During development and builds, Webpack will generate assets, and the URLs for those generated assets will be automatically injected into this template to render the final HTML. 65 | 66 | ### `package.json` 67 | 68 | The NPM package meta file that contains all the build dependencies and [build commands](commands.md). 69 | -------------------------------------------------------------------------------- /docs/unit.md: -------------------------------------------------------------------------------- 1 | # Unit Testing 2 | 3 | An overview of the tools used by this boilerplate for unit testing: 4 | 5 | - [Karma](https://karma-runner.github.io/): the test runner that launches browsers, runs the tests and reports the results to us. 6 | - [karma-webpack](https://github.com/webpack/karma-webpack): the plugin for Karma that bundles our tests using Webpack. 7 | - [Mocha](https://mochajs.org/): the test framework that we write test specs with. 8 | - [Chai](http://chaijs.com/): test assertion library that provides better assertion syntax. 9 | - [Sinon](http://sinonjs.org/): test utility library that provides spies, stubs and mocks. 10 | 11 | Chai and Sinon are integrated using [karma-sinon-chai](https://github.com/kmees/karma-sinon-chai), so all Chai interfaces (`should`, `expect`, `assert`) and `sinon` are globally available in test files. 12 | 13 | And the files: 14 | 15 | - `index.js` 16 | 17 | This is the entry file used by `karma-webpack` to bundle all the test code and source code (for coverage purposes). You can ignore it for the most part. 18 | 19 | - `specs/` 20 | 21 | This directory is where you write your actual tests. You can use full ES2015+ and all supported Webpack loaders in your tests. 22 | 23 | - `karma.conf.js` 24 | 25 | This is the Karma configuration file. See [Karma docs](https://karma-runner.github.io/) for more details. 26 | 27 | ## Running Tests in More Browsers 28 | 29 | You can run the tests in multiple real browsers by installing more [karma launchers](https://karma-runner.github.io/1.0/config/browsers.html) and adjusting the `browsers` field in `test/unit/karma.conf.js`. 30 | 31 | ## Mocking Dependencies 32 | 33 | This boilerplate comes with [inject-loader](https://github.com/plasticine/inject-loader) installed by default. For usage with `*.vue` components, see [vue-loader docs on testing with mocks](http://vue-loader.vuejs.org/en/workflow/testing-with-mocks.html). 34 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ name }} 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /meta.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "helpers": { 3 | "if_or": function (v1, v2, options) { 4 | if (v1 || v2) { 5 | return options.fn(this); 6 | } 7 | 8 | return options.inverse(this); 9 | } 10 | }, 11 | "prompts": { 12 | "name": { 13 | "type": "string", 14 | "required": true, 15 | "message": "项目名称" 16 | }, 17 | "description": { 18 | "type": "string", 19 | "required": false, 20 | "message": "项目描述", 21 | "default": "基于Vue.js的前端项目" 22 | }, 23 | "author": { 24 | "type": "string", 25 | "message": "作者" 26 | }, 27 | "build": { 28 | "type": "list", 29 | "message": "Vue 编译", 30 | "choices": [ 31 | { 32 | "name": "运行时+编译器:推荐大多数用户使用", 33 | "value": "standalone", 34 | "short": "standalone" 35 | }, 36 | { 37 | "name": "运行时版本:约6KB的打包机最小+ gzip,但模板(或任何Vue特定的HTML)只允许在.vue文件中 - 渲染功能需要在其他地方", 38 | "value": "runtime", 39 | "short": "runtime" 40 | } 41 | ] 42 | }, 43 | "router": { 44 | "type": "confirm", 45 | "message": "安装 路由?" 46 | }, 47 | "lint": { 48 | "type": "confirm", 49 | "message": "使用 ESLint 规范 你的代码?" 50 | }, 51 | "lintConfig": { 52 | "when": "lint", 53 | "type": "list", 54 | "message": "选择一个默认ESLint标准", 55 | "choices": [ 56 | { 57 | "name": "Standard (https://github.com/standard/standard)", 58 | "value": "standard", 59 | "short": "Standard" 60 | }, 61 | { 62 | "name": "Airbnb (https://github.com/airbnb/javascript)", 63 | "value": "airbnb", 64 | "short": "Airbnb" 65 | }, 66 | { 67 | "name": "none (自定义配置)", 68 | "value": "none", 69 | "short": "none" 70 | } 71 | ] 72 | }, 73 | "vuesocket": { 74 | "type": "confirm", 75 | "message": "集成vue-socket.io开发响应式项目?" 76 | }, 77 | "socketio": { 78 | "type": "string", 79 | "message": "如果你选择集成vue-socket.io请输入你的远程服务端地址否则请忽略此项:" 80 | }, 81 | "unit": { 82 | "type": "confirm", 83 | "message": "安装测试框架 Karma + Mocha?" 84 | }, 85 | "e2e": { 86 | "type": "confirm", 87 | "message": "使用Nightwatch进行端到端测试?" 88 | } 89 | }, 90 | "filters": { 91 | ".eslintrc.js": "lint", 92 | ".eslintignore": "lint", 93 | "config/test.env.js": "unit || e2e", 94 | "test/unit/**/*": "unit", 95 | "build/webpack.test.conf.js": "unit", 96 | "test/e2e/**/*": "e2e", 97 | "src/router/**/*": "router" 98 | }, 99 | "completeMessage": "友情提示: 编程有害身体,为了你和家人的健康,请到野外编程\n\n {{^inPlace}}cd {{destDirName}}\n {{/inPlace}}npm install\n npm run dev\n\n如有疑问请联系QQ:121625933" 100 | }; 101 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-cli-template-webpack", 3 | "version": "2.0.0", 4 | "license": "MIT", 5 | "description": "A full-featured Webpack setup with hot-reload, lint-on-save, unit testing & css extraction.", 6 | "scripts": { 7 | "docs": "cd docs && gitbook serve", 8 | "docs:deploy": "bash ./deploy-docs.sh" 9 | }, 10 | "devDependencies": { 11 | "vue-cli": "^2.8.1" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /template/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-runtime"], 12 | "env": { 13 | "test": { 14 | "presets": ["env", "stage-2"], 15 | "plugins": ["istanbul"] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /template/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /template/.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /config/ 3 | /dist/ 4 | /*.js 5 | {{#unit}} 6 | /test/unit/coverage/ 7 | {{/unit}} 8 | -------------------------------------------------------------------------------- /template/.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parser: 'babel-eslint', 6 | parserOptions: { 7 | sourceType: 'module' 8 | }, 9 | env: { 10 | browser: true, 11 | }, 12 | {{#if_eq lintConfig "standard"}} 13 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md 14 | extends: 'standard', 15 | {{/if_eq}} 16 | {{#if_eq lintConfig "airbnb"}} 17 | extends: 'airbnb-base', 18 | {{/if_eq}} 19 | // required to lint *.vue files 20 | plugins: [ 21 | 'html' 22 | ], 23 | {{#if_eq lintConfig "airbnb"}} 24 | // check if imports actually resolve 25 | 'settings': { 26 | 'import/resolver': { 27 | 'webpack': { 28 | 'config': 'build/webpack.base.conf.js' 29 | } 30 | } 31 | }, 32 | {{/if_eq}} 33 | // add your custom rules here 34 | 'rules': { 35 | {{#if_eq lintConfig "standard"}} 36 | // allow paren-less arrow functions 37 | 'arrow-parens': 0, 38 | // allow async-await 39 | 'generator-star-spacing': 0, 40 | {{/if_eq}} 41 | {{#if_eq lintConfig "airbnb"}} 42 | // don't require .vue extension when importing 43 | 'import/extensions': ['error', 'always', { 44 | 'js': 'never', 45 | 'vue': 'never' 46 | }], 47 | // allow optionalDependencies 48 | 'import/no-extraneous-dependencies': ['error', { 49 | 'optionalDependencies': ['test/unit/index.js'] 50 | }], 51 | {{/if_eq}} 52 | // allow debugger during development 53 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /template/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | {{#unit}} 8 | /test/unit/coverage/ 9 | {{/unit}} 10 | {{#e2e}} 11 | /test/e2e/reports/ 12 | selenium-debug.log 13 | {{/e2e}} 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | -------------------------------------------------------------------------------- /template/.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | // to edit target browsers: use "browserslist" field in package.json 6 | "autoprefixer": {} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /template/README.md: -------------------------------------------------------------------------------- 1 | ## 编译 2 | 3 | ``` bash 4 | # 安装依赖 5 | npm install 6 | 7 | # 热重载 localhost:8080 8 | npm run dev 9 | 10 | # 生产模式(压缩) 11 | npm run build or npm run pro 12 | 13 | # 构建生产并查看捆绑分析器报告 14 | npm run build --report or npm run pro --report 15 | {{#unit}} 16 | # 运行单元测试 17 | npm run unit 18 | {{/unit}} 19 | {{#e2e}} 20 | 21 | # 运行e2e测试 22 | npm run e2e 23 | {{/e2e}} 24 | {{#if_or unit e2e}} 25 | 26 | # 运行所有测试 27 | npm test 28 | {{/if_or}} 29 | ``` 30 | # 项目说明 31 | 32 | ## 组件相关文件夹说明 (components) 33 | > 该文件夹建议只写组件 34 | 35 | `怎么判断是组件?` 36 | >是不是可以复用,原则上可以复用的需要抽离成组件,不能复用无需组件化. 37 | 38 | 39 | ## 配置相关文件夹说明 (config) 40 | 41 | ### `api.conf.js` 42 | - 功能: 43 | > 集中管理各个业务模块的后端请求地址,便于排错和集中管理 44 | - 用法: 45 | > 每个模块都导出一个本模块的地址对象即可 46 | ``` 47 | export const demo(模块名称) = { 48 | test: '/api/test' //只需要写相对路径路径 49 | } 50 | ``` 51 | 52 | ### `service.conf.js` 53 | - 功能: 按照项目的需求初始化一些服务的配置参数,例如请求`http://127.0.0.1/api/test`,则根路径(`baseUrl`)为`http://127.0.0.1`,这样在`api.conf.js`只需要写相对路径,其中```baseUrl```为必须参数 54 | > 55 | 56 | ### `apistore.js` 57 | - 功能: 58 | >   作为所有业务请求的入口,将所有的http相关的处理从页面组件中剥离,在服务模块中集中处理完复杂逻辑等操作,减少页面臃肿程度. 59 | - 页面调用方法:(我们将所有的服务模块全部包装到this.$api中,以便识别服务) 60 | ``` 61 | this.$api.user.adduser({name:'halower'}) 62 | ``` 63 | - 依赖文件: 64 | > `api.conf.js`、 `fetch(文件夹)` 65 | 66 | ### `baseapi.js` 67 | - 功能: 68 | > 作为所有服务对象的基类,包含了请求地址代理,统一的请求入口(`get,post` ect.) 69 | 70 | ### `routestore.js` 71 | - 功能: 72 | > 集中的路由管理中心,将各个业务模块中的路由统一整理合并 73 | - 用法: 74 | 1. 导入每个业务模块的路由单元 75 | ``` 76 | import demo from '@/pages/demo/z-routes 77 | ``` 78 | 2. 添加业务路由 79 | ``` 80 | routes = routes.concat(demo) 81 | ``` 82 | ## 指令相关文件夹说明 (directives) 83 | 84 | 功能 85 | 全局指令处理,只需要在此处使用Vue.directive即可扩展项目的指令集 86 | 87 | ## 过滤器相关文件夹说明 (filters) 88 | - 功能 89 | > 全局过滤器处理,只需要在此处扩展项目的过滤器集 90 | `Vue2.0中Vue作者和很多开发人员针对是否有必要保留过滤器曾在github上做过大量讨论,但是在目前的版本中依然支持过滤器,所以vbp也将其暂时引入进来` 91 | 92 | ## 业务服务相关文件夹说明 (fetch) 93 | - 功能 94 | > 将繁琐的数据处理和关联性的逻辑操作从页面中剥离,降低服务与页面的耦合 95 | - 用法 96 | 1. 引入服务基类并继承它 97 | 2. 方法中的url路径需要使用到`/config/api.conf.js` 的模块,由于已经使用代理处理过,因此只需要使用`this.业务模块名.请求地址`的模式即可以获取真实请求路径 98 | ``` 99 | import BaseApiController from '@/config/baseapi' 100 | 101 | /** 102 | * @class 演示业务功能 103 | */ 104 | class DemoApiController extends BaseApiController { 105 | /* 106 | * @method 这里我们只做演示 107 | */ 108 | test () { 109 | // 假设这里有很复杂的前端逻辑 110 | return this.get([this.demo.test], {}).then(res => { 111 | return res.data 112 | }) 113 | } 114 | } 115 | export default new DemoApiController() 116 | ``` 117 | - 依赖文件: 118 | > `api.conf.js` 119 | 120 | ## 业务页面相关文件夹 (pages) 121 | - 功能 122 | > 编写各个业务模块的页面,每个页面都可以独立配置自己的自己独立的业务路由 123 | - 约定 124 | > 125 | 1. 建议路由按照业务模块层级命名,可以有效地避免非规则的命名给我们造成的路由冲突的困扰 126 | 2. 每个业务模块下都使用z-routes.js命名路由的配置文件,这样做仅仅是统一的将路由文件至于 该文件夹的最底下,当然你可以不这么做。 127 | - 用法 128 | > 在配置完路由后,请按照config中的说明文档,将路由导入 routestore.js 129 | 130 | ## 状态相关管理文件夹 (store) 131 | - 功能 132 | > 统一管理项目状态,在复杂的交互项目中可以启用该模块,可以减少事件的不便性 133 | 134 | - 使用方法 135 | > 136 | 1. `mutation-types.js` 在这里进行mutation命名的统一管理 137 | ``` 138 | export const CHANGE_NAME = 'CHANGE_NAME' // 测试更改模块名称 139 | ``` 140 | 在各个模块中可以使用如下方式引用,使用 ` [types.CHANGE_NAME]` 方式获取 141 | 142 | ``` 143 | import * as types from '@/store/mutation-types' 144 | ``` 145 | 146 | 2. 在`modules`文件夹中编写对应的模块,可以引用`fetch`文件夹中的服务在actions中处理业务 147 | 3. 写完模块状态管理后,在`index.js`中引入相应模块 148 | ``` 149 | modules: { 150 | test 151 | } 152 | ``` 153 | 4. 页面使用 154 | ``` 155 | 162 | 163 | 178 | ``` 179 | 5. 说明 180 | - 模块中需要使用 `namespaced: true` 标记 181 | - 根节点状态可以直接在 `index.js` 编写 182 | - 能不用this.$store 最好不要使用,保持页面整洁 183 | 184 | 6. `eventbus.js` 主要用于简单的非父子组件交互通信使用 185 | > 使用方法 186 | ``` 187 | this.$eventbus.on(event: string | Array, fn: Function) 188 | this.$eventbus.emit(event: string, data: Object) 189 | ``` 190 | -------------------------------------------------------------------------------- /template/build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | process.env.NODE_ENV = 'production' 5 | 6 | const ora = require('ora') 7 | const rm = require('rimraf') 8 | const path = require('path') 9 | const chalk = require('chalk') 10 | const webpack = require('webpack') 11 | const config = require('../config') 12 | const webpackConfig = require('./webpack.prod.conf') 13 | 14 | const spinner = ora('building for production...') 15 | spinner.start() 16 | 17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 18 | if (err) throw err 19 | webpack(webpackConfig, function (err, stats) { 20 | spinner.stop() 21 | if (err) throw err 22 | process.stdout.write(stats.toString({ 23 | colors: true, 24 | modules: false, 25 | children: false, 26 | chunks: false, 27 | chunkModules: false 28 | }) + '\n\n') 29 | 30 | if (stats.hasErrors()) { 31 | console.log(chalk.red(' Build failed with errors.\n')) 32 | process.exit(1) 33 | } 34 | 35 | console.log(chalk.cyan(' Build complete.\n')) 36 | console.log(chalk.yellow( 37 | ' Tip: built files are meant to be served over an HTTP server.\n' + 38 | ' Opening index.html over file:// won\'t work.\n' 39 | )) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /template/build/check-versions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const chalk = require('chalk') 3 | const semver = require('semver') 4 | const packageConfig = require('../package.json') 5 | const shell = require('shelljs') 6 | function exec (cmd) { 7 | return require('child_process').execSync(cmd).toString().trim() 8 | } 9 | 10 | const versionRequirements = [ 11 | { 12 | name: 'node', 13 | currentVersion: semver.clean(process.version), 14 | versionRequirement: packageConfig.engines.node 15 | } 16 | ] 17 | 18 | if (shell.which('npm')) { 19 | versionRequirements.push({ 20 | name: 'npm', 21 | currentVersion: exec('npm --version'), 22 | versionRequirement: packageConfig.engines.npm 23 | }) 24 | } 25 | 26 | module.exports = function () { 27 | const warnings = [] 28 | for (let i = 0; i < versionRequirements.length; i++) { 29 | const mod = versionRequirements[i] 30 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 31 | warnings.push(mod.name + ': ' + 32 | chalk.red(mod.currentVersion) + ' should be ' + 33 | chalk.green(mod.versionRequirement) 34 | ) 35 | } 36 | } 37 | 38 | if (warnings.length) { 39 | console.log('') 40 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 41 | console.log() 42 | for (let i = 0; i < warnings.length; i++) { 43 | const warning = warnings[i] 44 | console.log(' ' + warning) 45 | } 46 | console.log() 47 | process.exit(1) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /template/build/dev-client.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 'use strict' 3 | require('eventsource-polyfill') 4 | var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') 5 | 6 | hotClient.subscribe(function (event) { 7 | if (event.action === 'reload') { 8 | window.location.reload() 9 | } 10 | }) 11 | -------------------------------------------------------------------------------- /template/build/dev-server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | const config = require('../config') 5 | if (!process.env.NODE_ENV) { 6 | process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV) 7 | } 8 | 9 | const opn = require('opn') 10 | const path = require('path') 11 | const express = require('express') 12 | const webpack = require('webpack') 13 | const proxyMiddleware = require('http-proxy-middleware') 14 | const webpackConfig = {{#if_or unit e2e}}(process.env.NODE_ENV === 'testing' || process.env.NODE_ENV === 'production') 15 | ? require('./webpack.prod.conf') 16 | : {{/if_or}}require('./webpack.dev.conf') 17 | 18 | // default port where dev server listens for incoming traffic 19 | const port = process.env.PORT || config.dev.port 20 | // automatically open browser, if not set will be false 21 | const autoOpenBrowser = !!config.dev.autoOpenBrowser 22 | // Define HTTP proxies to your custom API backend 23 | // https://github.com/chimurai/http-proxy-middleware 24 | const proxyTable = config.dev.proxyTable 25 | 26 | const app = express() 27 | const compiler = webpack(webpackConfig) 28 | 29 | const devMiddleware = require('webpack-dev-middleware')(compiler, { 30 | publicPath: webpackConfig.output.publicPath, 31 | quiet: true 32 | }) 33 | 34 | const hotMiddleware = require('webpack-hot-middleware')(compiler, { 35 | log: false, 36 | heartbeat: 2000 37 | }) 38 | // force page reload when html-webpack-plugin template changes 39 | // currently disabled until this is resolved: 40 | // https://github.com/jantimon/html-webpack-plugin/issues/680 41 | // compiler.plugin('compilation', function (compilation) { 42 | // compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { 43 | // hotMiddleware.publish({ action: 'reload' }) 44 | // cb() 45 | // }) 46 | // }) 47 | 48 | // enable hot-reload and state-preserving 49 | // compilation error display 50 | app.use(hotMiddleware) 51 | 52 | // proxy api requests 53 | Object.keys(proxyTable).forEach(function (context) { 54 | let options = proxyTable[context] 55 | if (typeof options === 'string') { 56 | options = { target: options } 57 | } 58 | app.use(proxyMiddleware(options.filter || context, options)) 59 | }) 60 | 61 | // handle fallback for HTML5 history API 62 | app.use(require('connect-history-api-fallback')()) 63 | 64 | // serve webpack bundle output 65 | app.use(devMiddleware) 66 | 67 | // serve pure static assets 68 | const staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) 69 | app.use(staticPath, express.static('./static')) 70 | 71 | const uri = 'http://localhost:' + port 72 | 73 | var _resolve 74 | var _reject 75 | var readyPromise = new Promise((resolve, reject) => { 76 | _resolve = resolve 77 | _reject = reject 78 | }) 79 | 80 | var server 81 | var portfinder = require('portfinder') 82 | portfinder.basePort = port 83 | 84 | console.log('> Starting dev server...') 85 | devMiddleware.waitUntilValid(() => { 86 | portfinder.getPort((err, port) => { 87 | if (err) { 88 | _reject(err) 89 | } 90 | process.env.PORT = port 91 | var uri = 'http://localhost:' + port 92 | console.log('> Listening at ' + uri + '\n') 93 | // when env is testing, don't need open it 94 | if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') { 95 | opn(uri) 96 | } 97 | server = app.listen(port) 98 | _resolve() 99 | }) 100 | }) 101 | 102 | module.exports = { 103 | ready: readyPromise, 104 | close: () => { 105 | server.close() 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /template/build/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const config = require('../config') 4 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 5 | 6 | exports.assetsPath = function (_path) { 7 | const assetsSubDirectory = process.env.NODE_ENV === 'production' 8 | ? config.build.assetsSubDirectory 9 | : config.dev.assetsSubDirectory 10 | return path.posix.join(assetsSubDirectory, _path) 11 | } 12 | 13 | exports.cssLoaders = function (options) { 14 | options = options || {} 15 | 16 | const cssLoader = { 17 | loader: 'css-loader', 18 | options: { 19 | minimize: process.env.NODE_ENV === 'production', 20 | sourceMap: options.sourceMap 21 | } 22 | } 23 | 24 | // generate loader string to be used with extract text plugin 25 | function generateLoaders (loader, loaderOptions) { 26 | const loaders = [cssLoader] 27 | if (loader) { 28 | loaders.push({ 29 | loader: loader + '-loader', 30 | options: Object.assign({}, loaderOptions, { 31 | sourceMap: options.sourceMap 32 | }) 33 | }) 34 | } 35 | 36 | // Extract CSS when that option is specified 37 | // (which is the case during production build) 38 | if (options.extract) { 39 | return ExtractTextPlugin.extract({ 40 | use: loaders, 41 | fallback: 'vue-style-loader' 42 | }) 43 | } else { 44 | return ['vue-style-loader'].concat(loaders) 45 | } 46 | } 47 | 48 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 49 | return { 50 | css: generateLoaders(), 51 | postcss: generateLoaders(), 52 | less: generateLoaders('less'), 53 | sass: generateLoaders('sass', { indentedSyntax: true }), 54 | scss: generateLoaders('sass'), 55 | stylus: generateLoaders('stylus'), 56 | styl: generateLoaders('stylus') 57 | } 58 | } 59 | 60 | // Generate loaders for standalone style files (outside of .vue) 61 | exports.styleLoaders = function (options) { 62 | const output = [] 63 | const loaders = exports.cssLoaders(options) 64 | for (const extension in loaders) { 65 | const loader = loaders[extension] 66 | output.push({ 67 | test: new RegExp('\\.' + extension + '$'), 68 | use: loader 69 | }) 70 | } 71 | return output 72 | } 73 | -------------------------------------------------------------------------------- /template/build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const config = require('../config') 4 | const isProduction = process.env.NODE_ENV === 'production' 5 | 6 | module.exports = { 7 | loaders: utils.cssLoaders({ 8 | sourceMap: isProduction 9 | ? config.build.productionSourceMap 10 | : config.dev.cssSourceMap, 11 | extract: isProduction 12 | }), 13 | transformToRequire: { 14 | video: 'src', 15 | source: 'src', 16 | img: 'src', 17 | image: 'xlink:href' 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /template/build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const config = require('../config') 5 | const vueLoaderConfig = require('./vue-loader.conf') 6 | 7 | function resolve (dir) { 8 | return path.join(__dirname, '..', dir) 9 | } 10 | 11 | module.exports = { 12 | entry: { 13 | app: './src/main.js' 14 | }, 15 | output: { 16 | path: config.build.assetsRoot, 17 | filename: '[name].js', 18 | publicPath: process.env.NODE_ENV === 'production' 19 | ? config.build.assetsPublicPath 20 | : config.dev.assetsPublicPath 21 | }, 22 | resolve: { 23 | extensions: ['.js', '.vue', '.json'], 24 | alias: { 25 | {{#if_eq build "standalone"}} 26 | 'vue$': 'vue/dist/vue.esm.js', 27 | {{/if_eq}} 28 | '@': resolve('src'), 29 | '#': resolve('static') 30 | } 31 | }, 32 | module: { 33 | rules: [ 34 | {{#lint}} 35 | { 36 | test: /\.(js|vue)$/, 37 | loader: 'eslint-loader', 38 | enforce: 'pre', 39 | include: [resolve('src'), resolve('test')], 40 | options: { 41 | fix: true, 42 | formatter: require('eslint-friendly-formatter') 43 | } 44 | }, 45 | {{/lint}} 46 | { 47 | test: /\.vue$/, 48 | loader: 'vue-loader', 49 | options: vueLoaderConfig 50 | }, 51 | { 52 | test: /\.js$/, 53 | loader: 'babel-loader', 54 | include: [resolve('src'), resolve('test')] 55 | }, 56 | { 57 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 58 | loader: 'url-loader', 59 | options: { 60 | limit: 10000, 61 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 62 | } 63 | }, 64 | { 65 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 66 | loader: 'url-loader', 67 | options: { 68 | limit: 10000, 69 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 70 | } 71 | }, 72 | { 73 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 74 | loader: 'url-loader', 75 | options: { 76 | limit: 10000, 77 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 78 | } 79 | } 80 | ] 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /template/build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const webpack = require('webpack') 4 | const config = require('../config') 5 | const merge = require('webpack-merge') 6 | const baseWebpackConfig = require('./webpack.base.conf') 7 | const HtmlWebpackPlugin = require('html-webpack-plugin') 8 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 9 | 10 | // add hot-reload related code to entry chunks 11 | Object.keys(baseWebpackConfig.entry).forEach(function (name) { 12 | baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) 13 | }) 14 | 15 | module.exports = merge(baseWebpackConfig, { 16 | module: { 17 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }) 18 | }, 19 | // cheap-module-eval-source-map is faster for development 20 | devtool: '#cheap-module-eval-source-map', 21 | plugins: [ 22 | new webpack.DefinePlugin({ 23 | 'process.env': config.dev.env 24 | }), 25 | // https://github.com/glenjamin/webpack-hot-middleware#installation--usage 26 | new webpack.HotModuleReplacementPlugin(), 27 | new webpack.NoEmitOnErrorsPlugin(), 28 | // https://github.com/ampedandwired/html-webpack-plugin 29 | new HtmlWebpackPlugin({ 30 | filename: 'index.html', 31 | template: 'index.html', 32 | inject: true 33 | }), 34 | new FriendlyErrorsPlugin() 35 | ] 36 | }) 37 | -------------------------------------------------------------------------------- /template/build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const webpack = require('webpack') 5 | const config = require('../config') 6 | const merge = require('webpack-merge') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') 12 | 13 | const env = {{#if_or unit e2e}}process.env.NODE_ENV === 'testing' 14 | ? require('../config/test.env') 15 | : {{/if_or}}config.build.env 16 | 17 | const webpackConfig = merge(baseWebpackConfig, { 18 | module: { 19 | rules: utils.styleLoaders({ 20 | sourceMap: config.build.productionSourceMap, 21 | extract: true 22 | }) 23 | }, 24 | devtool: config.build.productionSourceMap ? '#source-map' : false, 25 | output: { 26 | path: config.build.assetsRoot, 27 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 28 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 29 | }, 30 | plugins: [ 31 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 32 | new webpack.DefinePlugin({ 33 | 'process.env': env 34 | }), 35 | // UglifyJs do not support ES6+, you can also use babel-minify for better treeshaking: https://github.com/babel/minify 36 | new webpack.optimize.UglifyJsPlugin({ 37 | compress: { 38 | warnings: false 39 | }, 40 | sourceMap: true, 41 | parallel: true 42 | }), 43 | // extract css into its own file 44 | new ExtractTextPlugin({ 45 | filename: utils.assetsPath('css/[name].[contenthash].css') 46 | }), 47 | // Compress extracted CSS. We are using this plugin so that possible 48 | // duplicated CSS from different components can be deduped. 49 | new OptimizeCSSPlugin({ 50 | cssProcessorOptions: { 51 | safe: true 52 | } 53 | }), 54 | // generate dist index.html with correct asset hash for caching. 55 | // you can customize output by editing /index.html 56 | // see https://github.com/ampedandwired/html-webpack-plugin 57 | new HtmlWebpackPlugin({ 58 | filename: {{#if_or unit e2e}}process.env.NODE_ENV === 'testing' 59 | ? 'index.html' 60 | : {{/if_or}}config.build.index, 61 | template: 'index.html', 62 | inject: true, 63 | minify: { 64 | removeComments: true, 65 | collapseWhitespace: true, 66 | removeAttributeQuotes: true 67 | // more options: 68 | // https://github.com/kangax/html-minifier#options-quick-reference 69 | }, 70 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 71 | chunksSortMode: 'dependency' 72 | }), 73 | // keep module.id stable when vender modules does not change 74 | new webpack.HashedModuleIdsPlugin(), 75 | // split vendor js into its own file 76 | new webpack.optimize.CommonsChunkPlugin({ 77 | name: 'vendor', 78 | minChunks: function (module) { 79 | // any required modules inside node_modules are extracted to vendor 80 | return ( 81 | module.resource && 82 | /\.js$/.test(module.resource) && 83 | module.resource.indexOf( 84 | path.join(__dirname, '../node_modules') 85 | ) === 0 86 | ) 87 | } 88 | }), 89 | // extract webpack runtime and module manifest to its own file in order to 90 | // prevent vendor hash from being updated whenever app bundle is updated 91 | new webpack.optimize.CommonsChunkPlugin({ 92 | name: 'manifest', 93 | chunks: ['vendor'] 94 | }), 95 | // copy custom static assets 96 | new CopyWebpackPlugin([ 97 | { 98 | from: path.resolve(__dirname, '../static'), 99 | to: config.build.assetsSubDirectory, 100 | ignore: ['.*'] 101 | } 102 | ]) 103 | ] 104 | }) 105 | 106 | if (config.build.productionGzip) { 107 | const CompressionWebpackPlugin = require('compression-webpack-plugin') 108 | 109 | webpackConfig.plugins.push( 110 | new CompressionWebpackPlugin({ 111 | asset: '[path].gz[query]', 112 | algorithm: 'gzip', 113 | test: new RegExp( 114 | '\\.(' + 115 | config.build.productionGzipExtensions.join('|') + 116 | ')$' 117 | ), 118 | threshold: 10240, 119 | minRatio: 0.8 120 | }) 121 | ) 122 | } 123 | 124 | if (config.build.bundleAnalyzerReport) { 125 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 126 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 127 | } 128 | 129 | module.exports = webpackConfig 130 | -------------------------------------------------------------------------------- /template/build/webpack.test.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // This is the webpack config used for unit tests. 3 | 4 | const utils = require('./utils') 5 | const webpack = require('webpack') 6 | const merge = require('webpack-merge') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | 9 | const webpackConfig = merge(baseWebpackConfig, { 10 | // use inline sourcemap for karma-sourcemap-loader 11 | module: { 12 | rules: utils.styleLoaders() 13 | }, 14 | devtool: '#inline-source-map', 15 | resolveLoader: { 16 | alias: { 17 | // necessary to to make lang="scss" work in test when using vue-loader's ?inject option 18 | // see discussion at https://github.com/vuejs/vue-loader/issues/724 19 | 'scss-loader': 'sass-loader' 20 | } 21 | }, 22 | plugins: [ 23 | new webpack.DefinePlugin({ 24 | 'process.env': require('../config/test.env') 25 | }) 26 | ] 27 | }) 28 | 29 | // no need for app entry during tests 30 | delete webpackConfig.entry 31 | 32 | module.exports = webpackConfig 33 | -------------------------------------------------------------------------------- /template/config/_README.md: -------------------------------------------------------------------------------- 1 | ## 配置相关文件夹说明 2 | 3 | ### `req.conf.js` 4 | - 功能: 5 | > 集中管理各个业务模块的后端请求地址,便于排错和集中管理 6 | - 用法: 7 | > 每个模块都导出一个本模块的地址对象即可 8 | ``` 9 | export const demo(模块名称) = { 10 | test: '/api/test' //只需要写相对路径路径 11 | } 12 | ``` 13 | 14 | ### `service.conf.js` 15 | - 功能: 按照项目的需求初始化一些服务的配置参数,例如请求`http://127.0.0.1/api/test`,则根路径(`baseUrl`)为`http://127.0.0.1`,这样在`req.conf.js`只需要写相对路径,其中```baseUrl```为必须参数 16 | > 17 | 18 | ### `apistore.js` 19 | - 功能: 20 | > 作为所有业务请求的入口,将所有的http相关的处理从页面组件中剥离,在服务模块中集中处理完复杂逻辑等操作,减少页面臃肿成都. 21 | - 页面调用方法:(我们将所有的服务模块全部包装到this.$api中,以便识别服务) 22 | ``` 23 | this.$api.user.adduser({name:'halower'}) 24 | ``` 25 | - 依赖文件: 26 | > `req.conf.js`、 `fetch(文件夹)` 27 | 28 | ### `baseapi.js` 29 | - 功能: 30 | > 作为所有服务对象的基类,包含了请求地址代理,统一的请求入口(`get,post` ect.) 31 | 32 | ### `routestore.js` 33 | - 功能: 34 | > 集中的路由管理中心,将各个业务模块中的路由统一整理合并 35 | - 用法: 36 | 1. 导入每个业务模块的路由单元 37 | ``` 38 | import demo from '@/pages/demo/z-routes 39 | ``` 40 | 2. 添加业务路由 41 | ``` 42 | routes = routes.concat(demo) 43 | ``` -------------------------------------------------------------------------------- /template/config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /template/config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.1.3 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | build: { 9 | env: require('./prod.env'), 10 | index: path.resolve(__dirname, '../dist/index.html'), 11 | assetsRoot: path.resolve(__dirname, '../dist'), 12 | assetsSubDirectory: 'static', 13 | assetsPublicPath: '/', 14 | productionSourceMap: true, 15 | // Gzip off by default as many popular static hosts such as 16 | // Surge or Netlify already gzip all static assets for you. 17 | // Before setting to `true`, make sure to: 18 | // npm install --save-dev compression-webpack-plugin 19 | productionGzip: false, 20 | productionGzipExtensions: ['js', 'css'], 21 | // Run the build command with an extra argument to 22 | // View the bundle analyzer report after build finishes: 23 | // `npm run build --report` 24 | // Set to `true` or `false` to always turn it on or off 25 | bundleAnalyzerReport: process.env.npm_config_report 26 | }, 27 | dev: { 28 | env: require('./dev.env'), 29 | port: process.env.PORT || 8080, 30 | autoOpenBrowser: true, 31 | assetsSubDirectory: 'static', 32 | assetsPublicPath: '/', 33 | proxyTable: {}, 34 | // CSS Sourcemaps off by default because relative paths are "buggy" 35 | // with this option, according to the CSS-Loader README 36 | // (https://github.com/webpack/css-loader#sourcemaps) 37 | // In our experience, they generally work as expected, 38 | // just be aware of this issue when enabling this option. 39 | cssSourceMap: false 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /template/config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /template/config/test.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const devEnv = require('./dev.env') 4 | 5 | module.exports = merge(devEnv, { 6 | NODE_ENV: '"testing"' 7 | }) 8 | -------------------------------------------------------------------------------- /template/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ name }} 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{ name }}", 3 | "version": "1.0.0", 4 | "description": "{{ description }}", 5 | "author": "{{ author }}", 6 | "private": true, 7 | "scripts": { 8 | "dev": "node build/dev-server.js", 9 | "start": "npm run dev", 10 | "build": "node build/build.js"{{#unit}}, 11 | "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run"{{/unit}}{{#e2e}}, 12 | "e2e": "node test/e2e/runner.js"{{/e2e}}{{#if_or unit e2e}}, 13 | "test": "{{#unit}}npm run unit{{/unit}}{{#unit}}{{#e2e}} && {{/e2e}}{{/unit}}{{#e2e}}npm run e2e{{/e2e}}"{{/if_or}}{{#lint}}, 14 | "lint": "eslint --ext .js,.vue src{{#unit}} test/unit/specs{{/unit}}{{#e2e}} test/e2e/specs{{/e2e}}"{{/lint}} 15 | }, 16 | "dependencies": { 17 | "axios": "^0.17.0", 18 | "vue": "^2.5.2"{{#router}}, 19 | "vue-router": "^3.0.1"{{/router}}, 20 | "vuex": "^3.0.0"{{#router}}, 21 | "vuex-router-sync": "^5.0.0"{{/router}}, 22 | "vue-i18n": "^7.3.2"{{#vuesocket}}, 23 | "vue-socket.io": "^2.1.1-a"{{/vuesocket}} 24 | }, 25 | "devDependencies": { 26 | "autoprefixer": "^7.1.2", 27 | "babel-core": "^6.22.1", 28 | {{#lint}} 29 | "babel-eslint": "^7.1.1", 30 | {{/lint}} 31 | "babel-loader": "^7.1.1", 32 | "babel-plugin-transform-runtime": "^6.22.0", 33 | "babel-preset-env": "^1.3.2", 34 | "babel-preset-stage-2": "^6.22.0", 35 | "babel-register": "^6.22.0", 36 | "chalk": "^2.0.1", 37 | "connect-history-api-fallback": "^1.3.0", 38 | "copy-webpack-plugin": "^4.0.1", 39 | "css-loader": "^0.28.0", 40 | {{#lint}} 41 | "eslint": "^3.19.0", 42 | "eslint-friendly-formatter": "^3.0.0", 43 | "eslint-loader": "^1.7.1", 44 | "eslint-plugin-html": "^3.0.0", 45 | {{#if_eq lintConfig "standard"}} 46 | "eslint-config-standard": "^10.2.1", 47 | "eslint-plugin-promise": "^3.4.0", 48 | "eslint-plugin-standard": "^3.0.1", 49 | "eslint-plugin-import": "^2.7.0", 50 | "eslint-plugin-node": "^5.2.0", 51 | {{/if_eq}} 52 | {{#if_eq lintConfig "airbnb"}} 53 | "eslint-config-airbnb-base": "^11.3.0", 54 | "eslint-import-resolver-webpack": "^0.8.3", 55 | "eslint-plugin-import": "^2.7.0", 56 | {{/if_eq}} 57 | {{/lint}} 58 | "eventsource-polyfill": "^0.9.6", 59 | "express": "^4.14.1", 60 | "extract-text-webpack-plugin": "^3.0.0", 61 | "file-loader": "^1.1.4", 62 | "friendly-errors-webpack-plugin": "^1.6.1", 63 | "html-webpack-plugin": "^2.30.1", 64 | "http-proxy-middleware": "^0.17.3", 65 | "webpack-bundle-analyzer": "^2.9.0", 66 | {{#unit}} 67 | "cross-env": "^5.0.1", 68 | "karma": "^1.4.1", 69 | "karma-coverage": "^1.1.1", 70 | "karma-mocha": "^1.3.0", 71 | "karma-phantomjs-launcher": "^1.0.2", 72 | "karma-phantomjs-shim": "^1.4.0", 73 | "karma-sinon-chai": "^1.3.1", 74 | "karma-sourcemap-loader": "^0.3.7", 75 | "karma-spec-reporter": "0.0.31", 76 | "karma-webpack": "^2.0.2", 77 | "mocha": "^3.2.0", 78 | "chai": "^4.1.2", 79 | "sinon": "^4.0.0", 80 | "sinon-chai": "^2.8.0", 81 | "inject-loader": "^3.0.0", 82 | "babel-plugin-istanbul": "^4.1.1", 83 | "phantomjs-prebuilt": "^2.1.14", 84 | {{/unit}} 85 | {{#e2e}} 86 | "chromedriver": "^2.27.2", 87 | "cross-spawn": "^5.0.1", 88 | "nightwatch": "^0.9.12", 89 | "selenium-server": "^3.0.1", 90 | {{/e2e}} 91 | "semver": "^5.3.0", 92 | "shelljs": "^0.7.6", 93 | "opn": "^5.1.0", 94 | "optimize-css-assets-webpack-plugin": "^3.2.0", 95 | "ora": "^1.2.0", 96 | "rimraf": "^2.6.0", 97 | "url-loader": "^0.5.8", 98 | "vue-loader": "^13.3.0", 99 | "vue-style-loader": "^3.0.1", 100 | "vue-template-compiler": "^2.5.2", 101 | "portfinder": "^1.0.13", 102 | "webpack": "^3.6.0", 103 | "webpack-dev-middleware": "^1.12.0", 104 | "webpack-hot-middleware": "^2.18.2", 105 | "webpack-merge": "^4.1.0" 106 | }, 107 | "engines": { 108 | "node": ">= 4.0.0", 109 | "npm": ">= 3.0.0" 110 | }, 111 | "browserslist": [ 112 | "> 1%", 113 | "last 2 versions", 114 | "not ie <= 8" 115 | ] 116 | } 117 | -------------------------------------------------------------------------------- /template/src/App.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 29 | 39 | -------------------------------------------------------------------------------- /template/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halower/vbp/8cc9e6b82fcd50814d1667b87f7f9b61a937c0e5/template/src/assets/logo.png -------------------------------------------------------------------------------- /template/src/components/ProgressBar.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 86 | 87 | -------------------------------------------------------------------------------- /template/src/components/z_README.md: -------------------------------------------------------------------------------- 1 | ## 组件相关文件夹说明 (components) 2 | > 该文件夹建议只写组件 3 | 4 | `怎么判断是组件?` 5 | >是不是可以复用,原则上可以复用的需要抽离成组件,不能复用无需组件化. 6 | -------------------------------------------------------------------------------- /template/src/config/api/api.conf.js: -------------------------------------------------------------------------------- 1 | export const demo = { 2 | test: '/api/test' // 测试请求路径 3 | } 4 | -------------------------------------------------------------------------------- /template/src/config/api/api.store.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 各个服务模块统一仓储 3 | */ 4 | import demo from '@/fetch/demo-api' 5 | export const apistore = { 6 | demo 7 | } 8 | -------------------------------------------------------------------------------- /template/src/config/api/baseapi.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import envConf from '@/config/env/env.export' 3 | import * as request from '@/config/api/api.conf' 4 | 5 | export default class BaseApiController { 6 | constructor () { 7 | axios.defaults.timeout = 5000 8 | axios.defaults.baseURL = envConf.REMOTE_ADDR 9 | 10 | axios.interceptors.response.use(function (response) { 11 | return response 12 | }, function (error) { 13 | // todo: 做一些其他日志记录处理 14 | console.log({ 15 | data: error.response.data, 16 | status: error.response.status, 17 | headers: error.response.headers, 18 | message: error.message 19 | }); 20 | return Promise.reject(error) 21 | }) 22 | this._req = request 23 | Object.keys(this._req).forEach(key => this._proxy(key)) 24 | } 25 | 26 | _proxy (key) { 27 | let _ = this 28 | Object.defineProperty(_, key, { 29 | configurable: false, 30 | enumerable: true, 31 | get () { 32 | return _._req[key] 33 | }, 34 | set (newVal) { 35 | _.data[key] = newVal 36 | } 37 | }) 38 | } 39 | 40 | get (url, config) { 41 | return axios.get(url, config) 42 | } 43 | 44 | post (url, data, config) { 45 | return axios.post(url, data, config) 46 | } 47 | 48 | delete (url, config) { 49 | return axios.delete(url, config) 50 | } 51 | 52 | put (url, data, config) { 53 | return axios.put(url, data, config) 54 | } 55 | 56 | all (promises) { 57 | return Promise.all(promises) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /template/src/config/env/env.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "REMOTE_ADDR 为请求服务的根路径,非可选参数", 3 | "development": { 4 | "REMOTE_ADDR": "http://127.0.0.1" 5 | }, 6 | "production": { 7 | "REMOTE_ADDR": "http://127.0.0.1" 8 | }, 9 | "test": { 10 | "REMOTE_ADDR": "http://127.0.0.1" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /template/src/config/env/env.export.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 此文件无需开发者关注 3 | */ 4 | import ENV from '@/config/env/env.conf.json' 5 | let map = new Map() 6 | map.set('development', ENV.development) 7 | map.set('production', ENV.production) 8 | map.set('test', ENV.test) 9 | export default map.get(process.env.NODE_ENV) 10 | -------------------------------------------------------------------------------- /template/src/config/i18n/index.js: -------------------------------------------------------------------------------- 1 | import En from '@/languages/en' 2 | import Zh from '@/languages/zh' 3 | const languages = { 4 | en: En, 5 | zh: Zh 6 | } 7 | 8 | export default languages 9 | -------------------------------------------------------------------------------- /template/src/config/routes/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import routes from './routes.store' 4 | 5 | Vue.use(Router) 6 | 7 | export default new Router({ 8 | mode: 'history', 9 | scrollBehavior: (to, from, savedPosition) => { 10 | if (savedPosition) { 11 | return savedPosition 12 | } else { 13 | return { x: 0, y: 0 } 14 | } 15 | }, 16 | routes 17 | }) 18 | -------------------------------------------------------------------------------- /template/src/config/routes/routes.store.js: -------------------------------------------------------------------------------- 1 | import demo from '@/pages/demo/z-routes' 2 | 3 | let routes = [] 4 | // 添加业务路由 5 | routes = routes.concat(demo) 6 | 7 | export default routes 8 | -------------------------------------------------------------------------------- /template/src/config/z_README.md: -------------------------------------------------------------------------------- 1 | ## 配置相关文件夹说明 2 | 3 | ### `api.conf.js` 4 | - 功能: 5 | > 集中管理各个业务模块的后端请求地址,便于排错和集中管理 6 | - 用法: 7 | > 每个模块都导出一个本模块的地址对象即可 8 | ``` 9 | export const demo(模块名称) = { 10 | test: '/api/test' //只需要写相对路径路径 11 | } 12 | ``` 13 | 14 | ### `service.conf.js` 15 | - 功能: 按照项目的需求初始化一些服务的配置参数,例如请求`http://127.0.0.1/api/test`,则根路径(`baseUrl`)为`http://127.0.0.1`,这样在`api.conf.js`只需要写相对路径,其中```baseUrl```为必须参数 16 | > 17 | 18 | ### `api.store.js` 19 | - 功能: 20 | >   作为所有业务请求的入口,将所有的http相关的处理从页面组件中剥离,在服务模块中集中处理完复杂逻辑等操作,减少页面臃肿程度. 21 | - 页面调用方法:(我们将所有的服务模块全部包装到this.$api中,以便识别服务) 22 | ``` 23 | this.$api.user.adduser({name:'halower'}) 24 | ``` 25 | - 依赖文件: 26 | > `api.conf.js`、 `fetch(文件夹)` 27 | 28 | ### `baseapi.js` 29 | - 功能: 30 | > 作为所有服务对象的基类,包含了请求地址代理,统一的请求入口(`get,post` ect.) 31 | 32 | ### `routestore.js` 33 | - 功能: 34 | > 集中的路由管理中心,将各个业务模块中的路由统一整理合并 35 | - 用法: 36 | 1. 导入每个业务模块的路由单元 37 | ``` 38 | import demo from '@/pages/demo/z-routes 39 | ``` 40 | 2. 添加业务路由 41 | ``` 42 | routes = routes.concat(demo) 43 | ``` 44 | -------------------------------------------------------------------------------- /template/src/directives/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | install (Vue, options) { 3 | /** 4 | * @directive 图片未加载前显示随机背景 5 | */ 6 | Vue.directive('lazyimg', { 7 | inserted (el, binding) { 8 | let color = Math.floor(Math.random() * 1000000) 9 | el.style.backgroundColor = '#' + color 10 | let img = new Image() 11 | img.src = binding.value 12 | img.onload = function () { 13 | let factSrc = binding.value 14 | el.style.backgroundImage = 'url(' + factSrc + ')' 15 | } 16 | } 17 | }) 18 | 19 | /** 20 | * @directive 点击外部区域 21 | */ 22 | Vue.directive('clickoutside', { 23 | inserted (el, binding) { 24 | let documentHandler = (e) => { 25 | if (el.contains(e.target)) { 26 | return false 27 | } 28 | if (binding.expression) { 29 | binding.value(e) 30 | } 31 | } 32 | el.__vueClickOutside__ = documentHandler 33 | document.addEventListener('click', documentHandler) 34 | }, 35 | unbind (el) { 36 | document.removeEventListener('click', el.__vueClickOutside__) 37 | delete el.__vueClickOutside__ 38 | } 39 | }) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /template/src/directives/z_README.md: -------------------------------------------------------------------------------- 1 | ## 指令相关文件夹说明 (directives) 2 | - 功能 3 | > 全局指令处理,只需要在此处使用`Vue.directive`即可扩展项目的指令集 -------------------------------------------------------------------------------- /template/src/fetch/demo-api.js: -------------------------------------------------------------------------------- 1 | import BaseApiController from '@/config/api/baseapi' 2 | 3 | /** 4 | * @class 演示业务功能 5 | */ 6 | class DemoApiController extends BaseApiController { 7 | /* 8 | * @method 这里我们只做演示 9 | * @description 假设这里有很复杂的前端逻辑 10 | */ 11 | test = params => { return this.get(this.demo.test, params).then(res => res.data) } 12 | } 13 | export default new DemoApiController() 14 | -------------------------------------------------------------------------------- /template/src/fetch/z_README.md: -------------------------------------------------------------------------------- 1 | ## 业务服务相关文件夹说明 (directives) 2 | - 功能 3 | > 将繁琐的数据处理和关联性的逻辑操作从页面中剥离,降低服务与页面的耦合 4 | - 用法 5 | 1. 引入服务基类并继承它 6 | 2. 方法中的url路径需要使用到`/config/req.conf.js` 的模块,由于已经使用代理处理过,因此只需要使用`this.业务模块名.请求地址`的模式即可以获取真实请求路径 7 | ``` 8 | import BaseApiController from '@/config/baseapi' 9 | 10 | /** 11 | * @class 演示业务功能 12 | */ 13 | class DemoApiController extends BaseApiController { 14 | /* 15 | * @method 这里我们只做演示 16 | */ 17 | test () { 18 | // 假设这里有很复杂的前端逻辑 19 | return this.get([this.demo.test], {}).then(res => { 20 | return res.data 21 | }) 22 | } 23 | } 24 | export default new DemoApiController() 25 | ``` 26 | - 依赖文件: 27 | > `req.conf.js` -------------------------------------------------------------------------------- /template/src/filters/index.js: -------------------------------------------------------------------------------- 1 | export function host (url) { 2 | const host = url.replace(/^https?:\/\//, '').replace(/\/.*$/, '') 3 | const parts = host.split('.').slice(-3) 4 | if (parts[0] === 'www') parts.shift() 5 | return parts.join('.') 6 | } 7 | 8 | export function timeAgo (time) { 9 | const between = Date.now() / 1000 - Number(time) 10 | if (between < 3600) { 11 | return pluralize(~~(between / 60), ' minute') 12 | } else if (between < 86400) { 13 | return pluralize(~~(between / 3600), ' hour') 14 | } else { 15 | return pluralize(~~(between / 86400), ' day') 16 | } 17 | } 18 | 19 | function pluralize (time, label) { 20 | if (time === 1) { 21 | return time + label 22 | } 23 | return time + label + 's' 24 | } 25 | -------------------------------------------------------------------------------- /template/src/filters/z_README.md: -------------------------------------------------------------------------------- 1 | ## 过滤器相关文件夹说明 (filters) 2 | - 功能 3 | > 全局过滤器处理,只需要在此处扩展项目的过滤器集 -------------------------------------------------------------------------------- /template/src/languages/en.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | demo: { 3 | title: 'This page can be deleted in the actual development, this example is used to manage page status using vuex demo', 4 | currentcode: 'Current Code', 5 | precode: 'Previous Code', 6 | changecode: 'Change Code' 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /template/src/languages/z_README.md: -------------------------------------------------------------------------------- 1 | 建议按照不同的业务需要添加分组翻译,便于整理,添加不同的语言类型后在 ```config/i18n.js```引入相应的语言包 2 | -------------------------------------------------------------------------------- /template/src/languages/zh.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | demo: { 3 | title: '该页面在实际开发可以删除,本例用于使用vuex管理页面状态的演示', 4 | currentcode: '当前编码', 5 | precode: '之前编码', 6 | changecode: '改编编码' 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /template/src/main.js: -------------------------------------------------------------------------------- 1 | {{#if_eq build "standalone"}} 2 | {{/if_eq}} 3 | import Vue from 'vue'{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 4 | import App from './App'{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 5 | import mixins from '@/mixins'{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 6 | {{#router}} 7 | import router from '@/config/routes'{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 8 | import { sync } from 'vuex-router-sync'{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 9 | {{/router}} 10 | import { apistore } from '@/config/api/api.store'{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 11 | import directive from '@/directives'{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 12 | import * as filters from '@/filters'{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 13 | import { createStore } from '@/store'{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 14 | import eventbus from '@/store/eventbus'{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 15 | import VueI18n from 'vue-i18n'{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 16 | {{#vuesocket}} 17 | import VueSocketio from 'vue-socket.io'{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 18 | {{/vuesocket}} 19 | import messages from '@/config/i18n'{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 20 | import ProgressBar from '@/components/ProgressBar.vue'{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 21 | 22 | Object.keys(filters).forEach(key => { 23 | Vue.filter(key, filters[key]){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 24 | }) 25 | 26 | Vue.use(directive){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 27 | {{#router}} 28 | Vue.use(VueI18n){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 29 | Vue.mixin(mixins){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 30 | 31 | const store = createStore(){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 32 | sync(store, router){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 33 | {{/router}} 34 | {{#vuesocket}} 35 | Vue.use(VueSocketio, '{{socketio}}', store){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 36 | {{/vuesocket}} 37 | const i18n = new VueI18n({ 38 | locale: 'zh', 39 | messages 40 | }) 41 | 42 | const bar = Vue.prototype.$bar = new Vue(ProgressBar).$mount(){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 43 | document.body.appendChild(bar.$el){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 44 | 45 | Vue.prototype.$api = apistore{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 46 | Vue.prototype.$eventbus = eventbus{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 47 | Vue.config.productionTip = false{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 48 | Vue.config.devtools = true{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 49 | Vue.config.debug = true{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 50 | /* eslint-disable no-new */ 51 | new Vue({ 52 | el: '#app', 53 | store, 54 | i18n, 55 | {{#router}} 56 | router, 57 | {{/router}} 58 | {{#if_eq build "runtime"}} 59 | render: h => h(App){{#if_eq lintConfig "airbnb"}},{{/if_eq}} 60 | {{/if_eq}} 61 | {{#if_eq build "standalone"}} 62 | template: '', 63 | components: { App }{{#if_eq lintConfig "airbnb"}},{{/if_eq}} 64 | {{/if_eq}} 65 | }){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 66 | -------------------------------------------------------------------------------- /template/src/mixins/index.js: -------------------------------------------------------------------------------- 1 | const Mixins = { 2 | mounted () { 3 | if (this.$bar) this.$bar.start() 4 | } 5 | } 6 | 7 | export default Mixins 8 | -------------------------------------------------------------------------------- /template/src/pages/demo/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 38 | -------------------------------------------------------------------------------- /template/src/pages/demo/z-routes.js: -------------------------------------------------------------------------------- 1 | const routes = [ 2 | { 3 | path: '/', 4 | component: () => import('@/pages/demo/index') 5 | } 6 | ] 7 | 8 | export default routes 9 | -------------------------------------------------------------------------------- /template/src/pages/z_README.md: -------------------------------------------------------------------------------- 1 | ## 业务页面相关文件夹 (pages) 2 | - 功能 3 | > 编写各个业务模块的页面,每个页面都可以独立配置自己的自己独立的业务路由 4 | - 约定 5 | > 6 | 1. 建议路由按照业务模块层级命名,可以有效地避免非规则的命名给我们造成的路由冲突的困扰 7 | 2. 每个业务模块下都使用z-routes.js命名路由的配置文件,这样做仅仅是统一的将路由文件至于 该文件夹的最底下,当然你可以不这么做。 8 | - 用法 9 | > 在配置完路由后,请按照config中的说明文档,将路由导入 routestore.js -------------------------------------------------------------------------------- /template/src/store/eventbus.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | const bus = new Vue() 3 | 4 | const eventbus = { 5 | on (event, fn, once = false) { 6 | if (once) { 7 | bus.$once(event, fn) 8 | return false 9 | } 10 | bus.$on(event, fn) 11 | }, 12 | emit (event, data) { 13 | bus.$emit(event, data) 14 | } 15 | } 16 | 17 | export default eventbus 18 | -------------------------------------------------------------------------------- /template/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | import demo from './modules/demo' 5 | import socketDemo from './modules/socket_io_demo' 6 | Vue.use(Vuex) 7 | 8 | export const mutations = { 9 | {{#vuesocket}} 10 | initSocket: (state, socket) => { 11 | state.socket = socket 12 | } 13 | {{/vuesocket}} 14 | } 15 | 16 | export const actions = { 17 | } 18 | 19 | export const getters = { 20 | } 21 | 22 | export function createStore () { 23 | return new Vuex.Store({ 24 | modules: { 25 | demo, 26 | socketDemo 27 | }, 28 | {{#vuesocket}} 29 | state: {socket: {}}, 30 | {{else}} 31 | state: {}, 32 | {{/vuesocket}} 33 | actions, 34 | mutations, 35 | getters 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /template/src/store/modules/demo.js: -------------------------------------------------------------------------------- 1 | import * as types from '@/store/mutation-types' 2 | 3 | export default{ 4 | namespaced: true, 5 | state: { 6 | code: '000000', 7 | oldcode: '' 8 | }, 9 | getters: { 10 | precode: state => state.oldcode ? state.oldcode : '--------' 11 | }, 12 | mutations: { 13 | [types.CHANGE_CODE] (state) { 14 | state.oldcode = state.code 15 | state.code = Math.ceil(Math.random() * 100000) 16 | } 17 | }, 18 | actions: { 19 | changeCode ({commit}) { 20 | commit(types.CHANGE_CODE) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /template/src/store/modules/socket_io_demo.js: -------------------------------------------------------------------------------- 1 | export default { 2 | namespaced: true, 3 | state: { 4 | connect: false, 5 | serverMsg: null 6 | }, 7 | mutations: { 8 | SOCKET_CONNECT: (state, status) => { 9 | state.connect = true 10 | console.log('socket server have been connected') 11 | }, 12 | // Socket mutations always have SOCKET_ prefix. to listen to server emit event 13 | SOCKET_SERVEREVENT: (state, msg) => { 14 | state.serverMsg = msg 15 | } 16 | }, 17 | actions: { 18 | tell: ({ state, commit, rootState }, message) => { 19 | rootState.socket.emit('clientEvent', message) 20 | } 21 | } 22 | } 23 | 24 | /** Server Side Test Code **/ 25 | // var app = require('http').createServer() 26 | // var io = require('socket.io')(app); 27 | // var fs = require('fs'); 28 | 29 | // app.listen(80); 30 | 31 | // io.on('connect', function (socket) { 32 | // socket.emit('serverEvent', { res : 'welcome to use vbp freamwork' }); 33 | // socket.on('clientEvent', function (data) { 34 | // console.log(data); 35 | // }); 36 | // }); 37 | /** Server Side Test Code **/ 38 | -------------------------------------------------------------------------------- /template/src/store/mutation-types.js: -------------------------------------------------------------------------------- 1 | // 测试模块 2 | export const CHANGE_CODE = 'CHANGE_CODE' // 测试更改模块编码 3 | -------------------------------------------------------------------------------- /template/src/store/z_README.md: -------------------------------------------------------------------------------- 1 | ## 状态相关管理文件夹 (store) 2 | - 功能 3 | > 统一管理项目状态,在复杂的交互项目中可以启用该模块,可以减少事件的不便性 4 | 5 | - 使用方法 6 | > 7 | 1. `mutation-types.js` 在这里进行mutation命名的统一管理 8 | ``` 9 | export const CHANGE_NAME = 'CHANGE_NAME' // 测试更改模块名称 10 | ``` 11 | 在各个模块中可以使用如下方式引用,使用 ` [types.CHANGE_NAME]` 方式获取 12 | 13 | ``` 14 | import * as types from '@/store/mutation-types' 15 | ``` 16 | 17 | 2. 在`modules`文件夹中编写对应的模块,可以引用`fetch`文件夹中的服务在actions中处理业务 18 | 3. 写完模块状态管理后,在`index.js`中引入相应模块 19 | ``` 20 | modules: { 21 | test 22 | } 23 | ``` 24 | 4. 页面使用 25 | ``` 26 | 33 | 34 | 49 | ``` 50 | 5. 说明 51 | - 模块中需要使用 `namespaced: true` 标记 52 | - 根节点状态可以直接在 `index.js` 编写 53 | - 能不用this.$store 最好不要使用,保持页面整洁 54 | 55 | 6. `eventbus.js` 主要用于简单的非父子组件交互通信使用 56 | > 使用方法 57 | ``` 58 | this.$eventbus.on(event: string | Array, fn: Function) 59 | this.$eventbus.emit(event: string, data: Object) 60 | ``` -------------------------------------------------------------------------------- /template/static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halower/vbp/8cc9e6b82fcd50814d1667b87f7f9b61a937c0e5/template/static/.gitkeep -------------------------------------------------------------------------------- /template/test/e2e/custom-assertions/elementCount.js: -------------------------------------------------------------------------------- 1 | // A custom Nightwatch assertion. 2 | // the name of the method is the filename. 3 | // can be used in tests like this: 4 | // 5 | // browser.assert.elementCount(selector, count) 6 | // 7 | // for how to write custom assertions see 8 | // http://nightwatchjs.org/guide#writing-custom-assertions 9 | exports.assertion = function (selector, count) { 10 | this.message = 'Testing if element <' + selector + '> has count: ' + count{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 11 | this.expected = count{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 12 | this.pass = function (val) { 13 | return val === this.expected{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 14 | } 15 | this.value = function (res) { 16 | return res.value{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 17 | } 18 | this.command = function (cb) { 19 | var self = this{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 20 | return this.api.execute(function (selector) { 21 | return document.querySelectorAll(selector).length{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 22 | }, [selector], function (res) { 23 | cb.call(self, res){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 24 | }){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /template/test/e2e/nightwatch.conf.js: -------------------------------------------------------------------------------- 1 | require('babel-register') 2 | var config = require('../../config') 3 | 4 | // http://nightwatchjs.org/gettingstarted#settings-file 5 | module.exports = { 6 | src_folders: ['test/e2e/specs'], 7 | output_folder: 'test/e2e/reports', 8 | custom_assertions_path: ['test/e2e/custom-assertions'], 9 | 10 | selenium: { 11 | start_process: true, 12 | server_path: require('selenium-server').path, 13 | host: '127.0.0.1', 14 | port: 4444, 15 | cli_args: { 16 | 'webdriver.chrome.driver': require('chromedriver').path 17 | } 18 | }, 19 | 20 | test_settings: { 21 | default: { 22 | selenium_port: 4444, 23 | selenium_host: 'localhost', 24 | silent: true, 25 | globals: { 26 | devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port) 27 | } 28 | }, 29 | 30 | chrome: { 31 | desiredCapabilities: { 32 | browserName: 'chrome', 33 | javascriptEnabled: true, 34 | acceptSslCerts: true 35 | } 36 | }, 37 | 38 | firefox: { 39 | desiredCapabilities: { 40 | browserName: 'firefox', 41 | javascriptEnabled: true, 42 | acceptSslCerts: true 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /template/test/e2e/runner.js: -------------------------------------------------------------------------------- 1 | // 1. start the dev server using production config 2 | process.env.NODE_ENV = 'testing'{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 3 | var server = require('../../build/dev-server.js'){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 4 | 5 | server.ready.then(() => { 6 | // 2. run the nightwatch test suite against it 7 | // to run in additional browsers: 8 | // 1. add an entry in test/e2e/nightwatch.conf.json under "test_settings" 9 | // 2. add it to the --env flag below 10 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox` 11 | // For more information on Nightwatch's config file, see 12 | // http://nightwatchjs.org/guide#settings-file 13 | var opts = process.argv.slice(2){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 14 | if (opts.indexOf('--config') === -1) { 15 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js']){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 16 | } 17 | if (opts.indexOf('--env') === -1) { 18 | opts = opts.concat(['--env', 'chrome']){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 19 | } 20 | 21 | var spawn = require('cross-spawn'){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 22 | var runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' }){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 23 | 24 | runner.on('exit', function (code) { 25 | server.close(){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 26 | process.exit(code){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 27 | }){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 28 | 29 | runner.on('error', function (err) { 30 | server.close(){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 31 | throw err{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 32 | }){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 33 | }){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 34 | -------------------------------------------------------------------------------- /template/test/e2e/specs/test.js: -------------------------------------------------------------------------------- 1 | // For authoring Nightwatch tests, see 2 | // http://nightwatchjs.org/guide#usage 3 | 4 | module.exports = { 5 | 'default e2e tests': function {{#if_eq lintConfig "airbnb"}}test{{/if_eq}}(browser) { 6 | // automatically uses dev Server port from /config.index.js 7 | // default: http://localhost:8080 8 | // see nightwatch.conf.js 9 | const devServer = browser.globals.devServerURL{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 10 | 11 | browser 12 | .url(devServer) 13 | .waitForElementVisible('#app', 5000) 14 | .assert.elementPresent('.hello') 15 | .assert.containsText('h1', 'Welcome to Your Vue.js App') 16 | .assert.elementCount('img', 1) 17 | .end(){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 18 | }{{#if_eq lintConfig "airbnb"}},{{/if_eq}} 19 | }{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 20 | -------------------------------------------------------------------------------- /template/test/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "globals": { 6 | "expect": true, 7 | "sinon": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /template/test/unit/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 2 | 3 | Vue.config.productionTip = false{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 4 | 5 | // require all test files (files that ends with .spec.js) 6 | const testsContext = require.context('./specs', true, /\.spec$/){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 7 | testsContext.keys().forEach(testsContext){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 8 | 9 | // require all src files except main.js for coverage. 10 | // you can also change this to match only the subset of files that 11 | // you want coverage for. 12 | const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 13 | srcContext.keys().forEach(srcContext){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 14 | -------------------------------------------------------------------------------- /template/test/unit/karma.conf.js: -------------------------------------------------------------------------------- 1 | // This is a karma config file. For more details see 2 | // http://karma-runner.github.io/0.13/config/configuration-file.html 3 | // we are also using it with karma-webpack 4 | // https://github.com/webpack/karma-webpack 5 | 6 | var webpackConfig = require('../../build/webpack.test.conf'){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 7 | 8 | module.exports = function (config) { 9 | config.set({ 10 | // to run in additional browsers: 11 | // 1. install corresponding karma launcher 12 | // http://karma-runner.github.io/0.13/config/browsers.html 13 | // 2. add it to the `browsers` array below. 14 | browsers: ['PhantomJS'], 15 | frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'], 16 | reporters: ['spec', 'coverage'], 17 | files: ['./index.js'], 18 | preprocessors: { 19 | './index.js': ['webpack', 'sourcemap'] 20 | }, 21 | webpack: webpackConfig, 22 | webpackMiddleware: { 23 | noInfo: true{{#if_eq lintConfig "airbnb"}},{{/if_eq}} 24 | }, 25 | coverageReporter: { 26 | dir: './coverage', 27 | reporters: [ 28 | { type: 'lcov', subdir: '.' }, 29 | { type: 'text-summary' }{{#if_eq lintConfig "airbnb"}},{{/if_eq}} 30 | ] 31 | }{{#if_eq lintConfig "airbnb"}},{{/if_eq}} 32 | }){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 33 | }{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 34 | -------------------------------------------------------------------------------- /template/test/unit/specs/Hello.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 2 | import HelloWorld from '@/components/HelloWorld'{{#if_eq lintConfig "airbnb"}};{{/if_eq}} 3 | 4 | describe('HelloWorld.vue', () => { 5 | it('should render correct contents', () => { 6 | const Constructor = Vue.extend(HelloWorld){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 7 | const vm = new Constructor().$mount(){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 8 | expect(vm.$el.querySelector('.hello h1').textContent) 9 | .to.equal('Welcome to Your Vue.js App'){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 10 | }){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 11 | }){{#if_eq lintConfig "airbnb"}};{{/if_eq}} 12 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | 3 | yes "" | ./node_modules/.bin/vue init . test 4 | 5 | cd test 6 | npm install 7 | npm run lint 8 | npm test 9 | npm run build 10 | --------------------------------------------------------------------------------