├── static └── .gitkeep ├── src ├── device │ ├── mobile │ │ ├── index.js │ │ └── index.html │ ├── pc │ │ ├── api │ │ │ ├── middleware.js │ │ │ └── home.js │ │ ├── components │ │ │ ├── Page404 │ │ │ │ └── Index.vue │ │ │ ├── Home │ │ │ │ ├── Body.vue │ │ │ │ └── Index.vue │ │ │ └── commonComponents │ │ │ │ └── Header.vue │ │ ├── index.html │ │ ├── types │ │ │ ├── root.js │ │ │ └── home.js │ │ ├── index.js │ │ ├── router │ │ │ └── index.js │ │ ├── App.vue │ │ └── store │ │ │ ├── index.js │ │ │ └── modules │ │ │ └── home.js │ └── app │ │ ├── index.js │ │ └── index.html ├── assets │ └── logo.png ├── api │ ├── middleware.js │ └── device-root.js ├── config.js └── components │ └── RootCommonComponent.vue ├── .eslintignore ├── config ├── prod.env.js ├── test.env.js ├── dev.env.js └── index.js ├── tree.txt ├── images ├── icon.png ├── show.gif ├── build.png ├── build-pc.png ├── pc-index.png ├── view-com.png ├── run-dev-url.png ├── vuex-api-com.png └── whole-view.png ├── test ├── unit │ ├── .eslintrc │ ├── specs │ │ └── Hello.spec.js │ ├── index.js │ └── karma.conf.js └── e2e │ ├── specs │ └── test.js │ ├── custom-assertions │ └── elementCount.js │ ├── runner.js │ └── nightwatch.conf.js ├── .gitignore ├── .editorconfig ├── .postcssrc.js ├── index.html ├── .babelrc ├── .eslintrc.js ├── sever └── prod-view-server.js ├── package.json └── README.md /static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/device/mobile/index.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | config/*.js 3 | -------------------------------------------------------------------------------- /src/device/pc/api/middleware.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 这里是pc这个模块的接口的中间件 3 | */ 4 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NODE_ENV: '"production"' 3 | } 4 | -------------------------------------------------------------------------------- /src/device/app/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/5/19. 3 | */ 4 | -------------------------------------------------------------------------------- /tree.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentmrlau/vue-multi-device-single-page/HEAD/tree.txt -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentmrlau/vue-multi-device-single-page/HEAD/images/icon.png -------------------------------------------------------------------------------- /images/show.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentmrlau/vue-multi-device-single-page/HEAD/images/show.gif -------------------------------------------------------------------------------- /images/build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentmrlau/vue-multi-device-single-page/HEAD/images/build.png -------------------------------------------------------------------------------- /images/build-pc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentmrlau/vue-multi-device-single-page/HEAD/images/build-pc.png -------------------------------------------------------------------------------- /images/pc-index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentmrlau/vue-multi-device-single-page/HEAD/images/pc-index.png -------------------------------------------------------------------------------- /images/view-com.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentmrlau/vue-multi-device-single-page/HEAD/images/view-com.png -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentmrlau/vue-multi-device-single-page/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /images/run-dev-url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentmrlau/vue-multi-device-single-page/HEAD/images/run-dev-url.png -------------------------------------------------------------------------------- /images/vuex-api-com.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentmrlau/vue-multi-device-single-page/HEAD/images/vuex-api-com.png -------------------------------------------------------------------------------- /images/whole-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentmrlau/vue-multi-device-single-page/HEAD/images/whole-view.png -------------------------------------------------------------------------------- /test/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "globals": { 6 | "expect": true, 7 | "sinon": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /config/test.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var devEnv = require('./dev.env') 3 | 4 | module.exports = merge(devEnv, { 5 | NODE_ENV: '"testing"' 6 | }) 7 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var prodEnv = require('./prod.env') 3 | 4 | module.exports = merge(prodEnv, { 5 | NODE_ENV: '"development"' 6 | }) 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | test/unit/coverage 8 | test/e2e/reports 9 | selenium-debug.log 10 | /.idea 11 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | // to edit target browsers: use "browserlist" field in package.json 6 | "autoprefixer": {} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/device/pc/components/Page404/Index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | -------------------------------------------------------------------------------- /src/api/middleware.js: -------------------------------------------------------------------------------- 1 | // 这里放公用的中间件 2 | 3 | export const SET_AUTHORIZATION = (request, next) => { 4 | // 处理请求 5 | request.headers.set('Authorization', 'vincent') 6 | next((response) => { 7 | // 处理返回的数据 8 | console.log(response) 9 | }) 10 | } 11 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | vue-multi-single-page 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { "modules": false }], 4 | "stage-2" 5 | ], 6 | "plugins": ["transform-runtime"], 7 | "comments": false, 8 | "env": { 9 | "test": { 10 | "presets": ["env", "stage-2"], 11 | "plugins": [ "istanbul" ] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 这里放全局的配置文件 3 | */ 4 | 5 | // 请求域名 6 | const PRODUCTION_HOSTNAME = 'http://localhost:3000' 7 | const DEVELOPMENT_HOSTNAME = 'http://localhost:3000' 8 | export const ROOT_HOST_NAME = process.env.NODE_ENV === 'production' 9 | ? PRODUCTION_HOSTNAME 10 | : DEVELOPMENT_HOSTNAME 11 | -------------------------------------------------------------------------------- /src/device/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | vue-multi-single-page-app 6 | 7 | 8 |

来自app/index.html的内容

9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/device/pc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | vue-multi-single-page-pc 6 | 7 | 8 |

来自pc/index.html的内容,我仅仅是个用来打包的模板

9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/device/mobile/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | vue-multi-single-page-mobile 6 | 7 | 8 |

来自mobile/index.html的内容

9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/RootCommonComponent.vue: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 18 | -------------------------------------------------------------------------------- /src/device/pc/types/root.js: -------------------------------------------------------------------------------- 1 | // 根级别的方法命名 2 | // 命名规则示例 3 | // ROOT_S_COUNT 4 | // ROOT:属于跟级别的 5 | // S:STATE缩写;其他的类似,如G:GETTER 6 | // COUNT:属性名 7 | 8 | export const ROOT_S_COUNT = 'ROOT_S_COUNT' 9 | export const ROOT_G_COUNT = 'ROOT_G_COUNT' 10 | export const ROOT_M_INCREMENT = 'ROOT_M_INCREMENT' 11 | export const ROOT_A_INCREMENT = 'ROOT_A_INCREMENT' 12 | 13 | -------------------------------------------------------------------------------- /src/device/pc/components/Home/Body.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 | 19 | -------------------------------------------------------------------------------- /test/unit/specs/Hello.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Hello from '@/components/Hello' 3 | 4 | describe('Hello.vue', () => { 5 | it('should render correct contents', () => { 6 | const Constructor = Vue.extend(Hello) 7 | const vm = new Constructor().$mount() 8 | expect(vm.$el.querySelector('.hello h1').textContent) 9 | .to.equal('Welcome to Your Vue.js App') 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /src/device/pc/components/commonComponents/Header.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 | 19 | -------------------------------------------------------------------------------- /src/device/pc/index.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import App from './App.vue' 5 | import router from './router' 6 | import store from './store' 7 | 8 | Vue.config.productionTip = false 9 | 10 | /* eslint-disable no-new */ 11 | new Vue({ 12 | el: '#app', 13 | router, 14 | store, 15 | template: '', 16 | components: { App } 17 | }) 18 | -------------------------------------------------------------------------------- /src/device/pc/types/home.js: -------------------------------------------------------------------------------- 1 | // home模块的方法命名 2 | // 命名规则示例 3 | // HOME_S_COUNT 4 | // HOME:属于跟级别的 5 | // S:STATE缩写;其他的类似,如G:GETTER 6 | // COUNT:属性名 7 | 8 | export const HOME_S_AUTH = 'HOME_S_AUTH' 9 | export const HOME_G_AUTH = 'HOME_G_AUTH' 10 | export const HOME_M_AUTH = 'HOME_M_AUTH' 11 | export const HOME_A_AUTH = 'HOME_A_AUTH' 12 | export const HOME_A_ROOT_COUNT = 'HOME_A_ROOT_COUNT' 13 | export const HOME_A_GET_AUTH = 'HOME_A_GET_AUTH' 14 | export const HOME_A_SERVER_COUNT = 'HOME_A_SERVER_COUNT' 15 | -------------------------------------------------------------------------------- /src/device/pc/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | 4 | Vue.use(Router) 5 | 6 | const Home = resolve => require(['./../components/Home/Index.vue'], resolve) 7 | const Page404 = resolve => require(['./../components/Page404/Index.vue'], resolve) 8 | 9 | export default new Router({ 10 | routes: [ 11 | { 12 | path: '/', 13 | name: 'Home', 14 | component: Home 15 | }, 16 | { 17 | path: '*', 18 | name: 'Page404', 19 | component: Page404 20 | } 21 | ] 22 | }) 23 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | Vue.config.productionTip = false 4 | 5 | // require all test files (files that ends with .spec.js) 6 | const testsContext = require.context('./specs', true, /\.spec$/) 7 | testsContext.keys().forEach(testsContext) 8 | 9 | // require all src files except index.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)?$)/) 13 | srcContext.keys().forEach(srcContext) 14 | -------------------------------------------------------------------------------- /src/api/device-root.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 这里放公用的api 3 | * 可以被不同的device使用 4 | */ 5 | import Vue from 'vue' 6 | import vueResource from 'vue-resource' 7 | 8 | // 引入中间件 9 | import {SET_AUTHORIZATION} from './middleware' 10 | // 引入配置端口 11 | import {ROOT_HOST_NAME} from './../config' 12 | 13 | Vue.use(vueResource) 14 | Vue.http.options.crossOrigin = true 15 | 16 | // 使用中间件 17 | Vue.http.interceptors.push(SET_AUTHORIZATION) 18 | 19 | // 输出方法 20 | export const ROOT_API_GET_AUTH = function (data, options = {}) { 21 | return Vue.http.post(ROOT_HOST_NAME + '/getAuth', data, { 22 | ...options 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /src/device/pc/api/home.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 这里是pc的home这个模块的api 3 | * */ 4 | 5 | import Vue from 'vue' 6 | import vueResource from 'vue-resource' 7 | 8 | // 引入中间件 9 | import {SET_AUTHORIZATION} from './middleware' 10 | // 引入配置端口 11 | import {ROOT_HOST_NAME} from './../../../config' 12 | 13 | Vue.use(vueResource) 14 | Vue.http.options.crossOrigin = true 15 | 16 | // 使用根级别的中间件 17 | Vue.http.interceptors.push(SET_AUTHORIZATION) 18 | 19 | // 输出方法 20 | export const HOME_API_COUNT = function (params, options = {}) { 21 | return Vue.http.get(ROOT_HOST_NAME + '/count', { 22 | params: params, 23 | ...options 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /src/device/pc/App.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 22 | 23 | 32 | -------------------------------------------------------------------------------- /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 (browser) { 6 | // automatically uses dev Server port from /config.pc.js 7 | // default: http://localhost:8080 8 | // see nightwatch.conf.js 9 | const devServer = browser.globals.devServerURL 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() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // http://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 | // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style 13 | extends: 'standard', 14 | // required to lint *.vue files 15 | plugins: [ 16 | 'html' 17 | ], 18 | // add your custom rules here 19 | 'rules': { 20 | // allow paren-less arrow functions 21 | 'arrow-parens': 0, 22 | // allow async-await 23 | 'generator-star-spacing': 0, 24 | // allow debugger during development 25 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/device/pc/store/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 这里存放pc的根级别的 store mutation action 和组装模块并导出 3 | */ 4 | 5 | import Vue from 'vue' 6 | import Vuex from 'vuex' 7 | 8 | // 引入type 9 | import * as rootTypes from './../types/root' 10 | // 引入模块 11 | import home from './modules/home.js' 12 | 13 | Vue.use(Vuex) 14 | 15 | export default new Vuex.Store({ 16 | state: { 17 | [rootTypes.ROOT_S_COUNT]: 0 18 | }, 19 | getters: { 20 | [rootTypes.ROOT_G_COUNT]: state => state[rootTypes.ROOT_S_COUNT] 21 | }, 22 | mutations: { 23 | [rootTypes.ROOT_M_INCREMENT]: (state, payload) => { 24 | state[rootTypes.ROOT_S_COUNT] += payload.add 25 | } 26 | }, 27 | actions: { 28 | [rootTypes.ROOT_A_INCREMENT]: ({commit}) => { 29 | commit({ 30 | type: rootTypes.ROOT_M_INCREMENT, 31 | add: 2 32 | }) 33 | } 34 | }, 35 | modules: { 36 | home 37 | } 38 | }) 39 | -------------------------------------------------------------------------------- /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 11 | this.expected = count 12 | this.pass = function (val) { 13 | return val === this.expected 14 | } 15 | this.value = function (res) { 16 | return res.value 17 | } 18 | this.command = function (cb) { 19 | var self = this 20 | return this.api.execute(function (selector) { 21 | return document.querySelectorAll(selector).length 22 | }, [selector], function (res) { 23 | cb.call(self, res) 24 | }) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sever/prod-view-server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 用于预览打包后的产品的服务器 3 | */ 4 | 'use strict' 5 | const express = require('express') 6 | const opn = require('opn') 7 | const bodyParser = require('body-parser') 8 | 9 | let app = express() 10 | app.use(bodyParser()) 11 | app.all('*', function (req, res, next) { 12 | res.header('Access-Control-Allow-Origin', '*'); 13 | res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With'); 14 | res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS'); 15 | if (req.method == 'OPTIONS') { 16 | res.send(200); 17 | } else { 18 | next(); 19 | } 20 | }); 21 | 22 | app.use('/',express.static('./dist/')) 23 | 24 | app.post('/getAuth', (req, res, next) => { 25 | res.json({ 26 | 'Auth': 'Vincent-sever-coder', 27 | 'recHeader': req.get('Authorization') 28 | }) 29 | }) 30 | 31 | app.get('/count', (req, res, next) => { 32 | res.json({ 33 | count: 10 34 | }) 35 | }) 36 | 37 | app.listen('3000', () => { 38 | console.log('listen AT :3000') 39 | opn('http://localhost:3000/') 40 | }) 41 | -------------------------------------------------------------------------------- /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') 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 24 | }, 25 | coverageReporter: { 26 | dir: './coverage', 27 | reporters: [ 28 | { type: 'lcov', subdir: '.' }, 29 | { type: 'text-summary' } 30 | ] 31 | } 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /test/e2e/runner.js: -------------------------------------------------------------------------------- 1 | // 1. start the dev server using production config 2 | process.env.NODE_ENV = 'testing' 3 | var server = require('../../build/dev-server.js') 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) 14 | if (opts.indexOf('--config') === -1) { 15 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js']) 16 | } 17 | if (opts.indexOf('--env') === -1) { 18 | opts = opts.concat(['--env', 'chrome']) 19 | } 20 | 21 | var spawn = require('cross-spawn') 22 | var runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' }) 23 | 24 | runner.on('exit', function (code) { 25 | server.close() 26 | process.exit(code) 27 | }) 28 | 29 | runner.on('error', function (err) { 30 | server.close() 31 | throw err 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/device/pc/store/modules/home.js: -------------------------------------------------------------------------------- 1 | // 这里写home这个模块的 store 2 | import * as homeTypes from './../../types/home' 3 | import * as rootTypes from './../../types/root' 4 | 5 | // 引入一个全局的api 6 | import {ROOT_API_GET_AUTH} from './../../../../api/device-root' 7 | // 引入一个属于这个模块的api 8 | import {HOME_API_COUNT} from './../../api/home' 9 | export default { 10 | state: { 11 | [homeTypes.HOME_S_AUTH]: '不存在的' 12 | }, 13 | getters: { 14 | [homeTypes.HOME_G_AUTH]: (states) => { 15 | return states[homeTypes.HOME_S_AUTH] 16 | } 17 | }, 18 | mutations: { 19 | [homeTypes.HOME_M_AUTH]: (states, payload) => { 20 | states[homeTypes.HOME_S_AUTH] = payload.auth 21 | } 22 | }, 23 | actions: { 24 | [homeTypes.HOME_A_ROOT_COUNT]: ({commit}) => { 25 | return commit({ 26 | type: rootTypes.ROOT_M_INCREMENT, 27 | add: 3 28 | }) 29 | }, 30 | [homeTypes.HOME_A_GET_AUTH]: ({commit}) => { 31 | console.log('HOME_A_GET_AUTH') 32 | ROOT_API_GET_AUTH({'TEST': 'BODY_STR'}).then(response => { 33 | // 请求成功 34 | console.log('ROOT_API_GET_AUTH done!', response) 35 | commit({ 36 | type: homeTypes.HOME_M_AUTH, 37 | auth: response.body.Auth 38 | }) 39 | }, response => { 40 | console.log('ROOT_API_GET_AUTH fail!', response) 41 | }) 42 | }, 43 | [homeTypes.HOME_A_SERVER_COUNT]: ({commit}) => { 44 | HOME_API_COUNT({'PRAMA_TEST': 'PRAMA1_STR'}).then(response => { 45 | // 成功 46 | console.log('HOME_API_COUNT done', response) 47 | commit({ 48 | type: rootTypes.ROOT_M_INCREMENT, 49 | add: response.body.count 50 | }) 51 | }, response => { 52 | // 失败 53 | console.log('HOME_API_COUNT fail', response) 54 | }) 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | // see http://vuejs-templates.github.io/webpack for documentation. 2 | var path = require('path') 3 | var device = process.env.DEVICE_ENV || 'undefined' 4 | 5 | 6 | // 入口模板路径 7 | var htmlTemplate = './src/device/' + device + '/index.html' 8 | 9 | module.exports = { 10 | build: { 11 | env: require('./prod.env'), 12 | // 加入html入口 13 | htmlTemplate: htmlTemplate, 14 | index: path.resolve(__dirname, '../dist' , device , 'index.html'), 15 | assetsRoot: path.resolve(__dirname, '../dist' , device), 16 | assetsSubDirectory: 'static', 17 | // 这里的路径改成相对路径 18 | // 原来是: assetsPublicPath: '/', 19 | assetsPublicPath: '', 20 | productionSourceMap: true, 21 | // Gzip off by default as many popular static hosts such as 22 | // Surge or Netlify already gzip all static assets for you. 23 | // Before setting to `true`, make sure to: 24 | // npm install --save-dev compression-webpack-plugin 25 | productionGzip: false, 26 | productionGzipExtensions: ['js', 'css'], 27 | // Run the build command with an extra argument to 28 | // View the bundle analyzer report after build finishes: 29 | // `npm run build --report` 30 | // Set to `true` or `false` to always turn it on or off 31 | bundleAnalyzerReport: process.env.npm_config_report 32 | }, 33 | dev: { 34 | env: require('./dev.env'), 35 | port: 8080, 36 | autoOpenBrowser: true, 37 | assetsSubDirectory: 'static', 38 | assetsPublicPath: '/', 39 | proxyTable: {}, 40 | // CSS Sourcemaps off by default because relative paths are "buggy" 41 | // with this option, according to the CSS-Loader README 42 | // (https://github.com/webpack/css-loader#sourcemaps) 43 | // In our experience, they generally work as expected, 44 | // just be aware of this issue when enabling this option. 45 | cssSourceMap: false 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/device/pc/components/Home/Index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 56 | 57 | 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-multi-single-page", 3 | "version": "1.0.0", 4 | "description": "hybrid multi page & single page Vue object", 5 | "author": "Vincent Lau 413893093@qq.com", 6 | "private": true, 7 | "scripts": { 8 | "dev": "node build/dev-server.js", 9 | "start": "node build/dev-server.js", 10 | "build": "node build/build.js", 11 | "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run", 12 | "e2e": "node test/e2e/runner.js", 13 | "test": "npm run unit && npm run e2e", 14 | "lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs", 15 | "build-all": "node build/build-all.js" 16 | }, 17 | "dependencies": { 18 | "vue": "^2.2.6", 19 | "vue-resource": "^1.3.1", 20 | "vue-router": "^2.3.1", 21 | "vuex": "^2.3.1" 22 | }, 23 | "devDependencies": { 24 | "autoprefixer": "^6.7.2", 25 | "babel-core": "^6.22.1", 26 | "babel-eslint": "^7.1.1", 27 | "babel-loader": "^6.2.10", 28 | "babel-plugin-istanbul": "^4.1.1", 29 | "babel-plugin-transform-runtime": "^6.22.0", 30 | "babel-polyfill": "^6.23.0", 31 | "babel-preset-env": "^1.3.2", 32 | "babel-preset-stage-2": "^6.22.0", 33 | "babel-register": "^6.22.0", 34 | "body-parser": "^1.17.2", 35 | "chai": "^3.5.0", 36 | "chalk": "^1.1.3", 37 | "child_process": "^1.0.2", 38 | "chromedriver": "^2.27.2", 39 | "colors": "^1.1.2", 40 | "connect-history-api-fallback": "^1.3.0", 41 | "copy-webpack-plugin": "^4.0.1", 42 | "cross-env": "^4.0.0", 43 | "cross-spawn": "^5.0.1", 44 | "css-loader": "^0.28.0", 45 | "eslint": "^3.19.0", 46 | "eslint-config-standard": "^6.2.1", 47 | "eslint-friendly-formatter": "^2.0.7", 48 | "eslint-loader": "^1.7.1", 49 | "eslint-plugin-html": "^2.0.0", 50 | "eslint-plugin-promise": "^3.4.0", 51 | "eslint-plugin-standard": "^2.0.1", 52 | "eventsource-polyfill": "^0.9.6", 53 | "express": "^4.14.1", 54 | "extract-text-webpack-plugin": "^2.0.0", 55 | "file-loader": "^0.11.1", 56 | "friendly-errors-webpack-plugin": "^1.1.3", 57 | "fs": "0.0.1-security", 58 | "glob": "^7.1.1", 59 | "html-webpack-plugin": "^2.28.0", 60 | "http-proxy-middleware": "^0.17.3", 61 | "inject-loader": "^3.0.0", 62 | "karma": "^1.4.1", 63 | "karma-coverage": "^1.1.1", 64 | "karma-mocha": "^1.3.0", 65 | "karma-phantomjs-launcher": "^1.0.2", 66 | "karma-phantomjs-shim": "^1.4.0", 67 | "karma-sinon-chai": "^1.3.1", 68 | "karma-sourcemap-loader": "^0.3.7", 69 | "karma-spec-reporter": "0.0.30", 70 | "karma-webpack": "^2.0.2", 71 | "lolex": "^1.5.2", 72 | "mocha": "^3.2.0", 73 | "nightwatch": "^0.9.12", 74 | "node-sass": "^4.5.3", 75 | "opn": "^4.0.2", 76 | "optimize-css-assets-webpack-plugin": "^1.3.0", 77 | "ora": "^1.2.0", 78 | "path": "^0.12.7", 79 | "phantomjs-prebuilt": "^2.1.14", 80 | "rimraf": "^2.6.0", 81 | "sass-loader": "^6.0.5", 82 | "selenium-server": "^3.0.1", 83 | "semver": "^5.3.0", 84 | "shelljs": "^0.7.6", 85 | "sinon": "^2.1.0", 86 | "sinon-chai": "^2.8.0", 87 | "url-loader": "^0.5.8", 88 | "vue-loader": "^11.3.4", 89 | "vue-style-loader": "^2.0.5", 90 | "vue-template-compiler": "^2.2.6", 91 | "webpack": "^2.3.3", 92 | "webpack-bundle-analyzer": "^2.2.1", 93 | "webpack-dev-middleware": "^1.10.0", 94 | "webpack-hot-middleware": "^2.18.0", 95 | "webpack-merge": "^4.1.0" 96 | }, 97 | "engines": { 98 | "node": ">= 4.0.0", 99 | "npm": ">= 3.0.0" 100 | }, 101 | "browserslist": [ 102 | "> 1%", 103 | "last 2 versions", 104 | "not ie <= 8" 105 | ] 106 | } 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-multi-device-single-page 2 | > 多个单页应用整合的vue工程的开发环境 3 | > vue工程的目录设置 4 | 5 | [![vue-cli](https://img.shields.io/badge/vueCli-v2.8.2-orange.svg)](https://cn.vuejs.org/) [![vue 2.0](https://img.shields.io/badge/vue-v2.2.6-green.svg)](https://cn.vuejs.org/) [![vue-resuorce](https://img.shields.io/badge/vueResource-v1.3.3-green.svg)](https://github.com/pagekit/vue-resource/blob/develop/docs/http.md) [![vue-router](https://img.shields.io/badge/vueRouter-v2.3.1-green.svg)](https://cn.vuejs.org/) [![vuex](https://img.shields.io/badge/vuex-v2.3.1-green.svg)](https://cn.vuejs.org/) 6 | 7 | ## 本文内容: 8 | 1. vue + vuex + vue-resuorce + vue-route 的工程 的目录设计 9 | 2. 基于 vue-cli 的 多个vue单页应用的开发环境 搭建 10 | ## 目录: 11 | > 一、开发环境使用 12 | > 13 | > 二、需求分析 14 | > 15 | > 三、开发思路 16 | > 17 | > 四、src目录设计及思路 18 | > 19 | > 五、开发环境开发 20 | > 21 | > 六、整个开发环境的目录注释 22 | 23 | ## 一、开发环境使用 24 | 25 | #### 多终端(页面)路径设置 26 | 1. 在src/device/目录下添加终端(页面)路径,如:src/device/pc/ 27 | 2. 在新添加的文件下加入这个终端(页面)使用的打包模板,命名为index.html,如:src/device/pc/index.html 28 | 3. 在新添加的文件下加入这个终端(页面)使用的入口文件,命名为index.js,如:src/device/pc/index.js 29 | 30 | #### build 打包 31 | > 打生产环境的包,会自动把不同终端的文件按终端名称分开 32 | 33 | ![build-pc示例图](https://github.com/vincentmrlau/vue-multi-device-single-page/blob/master/images/build.png?raw=true) 34 | > npm run build 'device' 35 | > 36 | > device : 接受的参数,在 `/build/device-conf.js`里面有限制 37 | > 38 | > 示例: `npm run build pc` 打一个pc端的包 39 | 40 | > npm run build-all 41 | > 42 | > 打所有终端的包 43 | 44 | #### dev 开发 45 | > npm run dev 46 | > 47 | > 开始进行调试,基于vue-cli的,所以基本是vue-cli的 48 | 49 | 1. 自动打开一个网页,从这里选择要调试的终端 50 | 51 | ![build-pc示例图](https://github.com/vincentmrlau/vue-multi-device-single-page/blob/master/images/run-dev-url.png?raw=true) 52 | 53 | 2. 开始调试 54 | 55 | ![index-pc](https://github.com/vincentmrlau/vue-multi-device-single-page/blob/master/images/pc-index.png?raw=true) 56 | 57 | ## 二、需求分析: 58 | #### 需求: 59 | 1. 要开发pc端 + 移动端 + app混合开发的 页面,每个页面都是单页应用 60 | 2. 为了节约开发成本,这几个端要共用一些组件,和 方法 61 | 3. 打包要方便,调试要方便 62 | 4. vue应用 63 | 64 | #### 几个问题: 65 | 1. vue-cli提供了非常好的开发环境,我能否在这个基础上整一整,解决掉需求 2 和 3 呢? 66 | 2. vue + vuex + vue-resuorce +vue-route 的工程目录应该怎么设计呢? 67 | 68 | > 面对这样的需求,我的理解是把多个单页应用融合到一个工程里面,下面是我的解决办法 69 | 70 | #### 这个工程是啥 71 | 72 | > github [https://github.com/vincentmrlau/vue-multi-device-single-page](https://github.com/vincentmrlau/vue-multi-device-single-page),欢迎交流 73 | > 74 | > 多端(也可以是多页)的单页应用的vue工程的开发环境,本质上是多个单页应用 75 | > 76 | > 基于vue,整合了vuex vue-resuorece vue-router 的开发目录设计 77 | > 78 | > 整个基于vue-cli生成的目录进行修改,除了test(正在研究)外的功能均可使用 79 | 80 | ## 三、开发思路 81 | 82 | 1、设置公用组件的目录 83 | 84 | 2、抽离api,分为公用的api和属于各个页面自己的api 85 | 86 | 3、每个单页应用vuex管理状态 87 | 88 | 4、可能会被多人同时编辑,如何尽量减少merge 89 | 90 | 5、针对这样的需求的src下面的目录应该怎么设计(第三部分有写) 91 | 92 | 6、针对需求配置开发环境(从第 部门开始是关于这个开发环境的) 93 | 94 | ## 四、src目录设计及思路 95 | > 介绍src的目录设置及其作用 96 | > 97 | > 介绍 界面-模板html-组件-store-接口 的关系 98 | 99 | #### 概况两图流 100 | 1. pc主页示意图 101 | 102 | ![主页示意图](https://github.com/vincentmrlau/vue-multi-device-single-page/blob/master/images/pc-index.png?raw=true) 103 | 104 | 2. 分析图(怎一个乱字了得) 105 | 106 | ![分析图](https://github.com/vincentmrlau/vue-multi-device-single-page/blob/master/images/whole-view.png?raw=true) 107 | 108 | 109 | #### 目录设置及其作用 110 | ``` 111 | ├─src # 源文件目录 112 | │ │ config.js 113 | │ │ 114 | │ ├─api # 多端共用的 api 115 | │ │ device-root.js 116 | │ │ middleware.js 117 | │ │ 118 | │ ├─assets # 多端共用的 资源 119 | │ │ logo.png 120 | │ │ 121 | │ ├─components # 多端共用的 组件 122 | │ │ RootCommonComponent.vue 123 | │ │ 124 | │ └─device # 设备入口 125 | │ ├─app # 混合开发的放这里了,也可以分 ios 和 安卓 126 | │ │ index.html # app专用的html模板,打包好的东西会自动注入 127 | │ │ index.js # app的入口文件 128 | │ │ 129 | │ ├─mobile # 这里放移动端的页面 ,下面的 index文件作用类似其他端 130 | │ │ index.html 131 | │ │ index.js 132 | │ │ 133 | │ └─pc # 这个目录下的都是pc端使用的,当然其他端要用也是可以的,哈哈 134 | │ │ App.vue # 入口组件 135 | │ │ index.html # 模板文件 136 | │ │ index.js # 入口文件 137 | │ │ 138 | │ ├─api # 分离开接口 139 | │ │ home.js # home这个模块用的接口 140 | │ │ middleware.js # 一些公用的中间件 141 | │ │ 142 | │ ├─assets # 资源 143 | │ ├─components # 组件 144 | │ │ ├─commonComponents # 公共组件 145 | │ │ │ Header.vue 146 | │ │ │ 147 | │ │ ├─Home # home这个模块用的组件 148 | │ │ │ Body.vue 149 | │ │ │ Index.vue 150 | │ │ │ 151 | │ │ └─Page404 # 404这个模块用的组件 152 | │ │ Index.vue 153 | │ │ 154 | │ ├─router # 路由 155 | │ │ index.js 156 | │ │ 157 | │ ├─store # vuex 的store 158 | │ │ │ index.js # 根级别的store + 模块组装 159 | │ │ │ 160 | │ │ └─modules # store 模块 161 | │ │ home.js # home这个模块使用的store 162 | │ │ 163 | │ └─types # 放类型名称 164 | │ home.js # home这个模块使用的 types 165 | │ root.js # 公用的types 166 | ``` 167 | 168 | #### 界面-模板-组件 的关系 169 | > 界面:最后展现在用户面前的 170 | > 171 | > 模板:用来注入打包的html文件 172 | > 173 | > 组件:编写的vue组件 174 | 175 | 他们的关系如图: 176 | 177 | ![view-components](https://github.com/vincentmrlau/vue-multi-device-single-page/blob/master/images/view-com.png?raw=true) 178 | 179 | #### 组件-store(vuex)-api(vue-resuorce) 的关系 180 | 1. 组件使用store: 181 | 1. 通过辅助函数(mapGetters,mapActions等)把store的属性映射到组件中使用 182 | 2. 组件通过action来提交mutation修改状态 183 | 3. 也可以通过$store来使用 184 | 2. 组件使用api: 185 | 1. 组件通过store的action使用api 186 | 3. store内部安排 187 | 1. 由mutation来修改状态 188 | 2. 由action来提交mutation 189 | 4. 由store的action来调用api 190 | 5. api里面分离开中间件,按需调用 191 | 192 | 看图看图 ↓↓↓ 193 | 194 | ![主页示意图](https://github.com/vincentmrlau/vue-multi-device-single-page/blob/master/images/vuex-api-com.png?raw=true) 195 | 196 | 197 | 198 | ## 五、开发环境开发 199 | > 在vue-cli v2.8.2生产的开发环境的基础上进行修改 200 | 201 | #### 新增加:build/device-conf.js 用来出路多终端(页面)开发相关问题 202 | 203 | ``` 204 | 205 | var chalk = require('chalk') 206 | var glob = require('glob') 207 | 208 | // 获取deviceList 209 | var deviceList = [] 210 | var deviceSrcArray = glob.sync('./src/device/*') 211 | for(var x in deviceSrcArray){ 212 | deviceList.push(deviceSrcArray[x].split('/')[3]) 213 | } 214 | 215 | // 检测是否在输入的参数是否在允许的list中 216 | var checkDevice = function () { 217 | var device = process.env.DEVICE_ENV 218 | var result = false 219 | // 检查deviceList是否有重复 220 | var hash = {} 221 | var repeatList = [] 222 | for(var l = 0;l < deviceList.length; l++){ 223 | if(hash[deviceList[l]]){ 224 | repeatList.push(deviceList[l]) 225 | } 226 | hash[deviceList[l]] = true 227 | } 228 | if(repeatList.length > 0){ 229 | console.log(chalk.bgRed('deviceList 有重复:')) 230 | console.log(chalk.bgRed(repeatList.toString())) 231 | return result 232 | } 233 | for(var i in deviceList){ 234 | if(device === deviceList[i]){ 235 | result = device 236 | break 237 | } 238 | } 239 | if(result === false){ 240 | console.log(chalk.bgRed('参数错误,允许的参数为:')) 241 | console.log(chalk.bgRed(deviceList.toString())) 242 | } 243 | return result 244 | } 245 | 246 | exports.deviceList = deviceList 247 | exports.checkDevice = checkDevice 248 | // 其他依赖 249 | exports.polyfills = ['babel-polyfill'] 250 | 251 | ``` 252 | #### 添加:/build/build-all.js 253 | > 内部根据 deviceList 产生运行build.js子进程,完成打包 254 | 255 | ``` 256 | var execFileSync = require('child_process').execFileSync; 257 | var path = require('path') 258 | var deviceList = require('./device-conf').deviceList || [] 259 | 260 | var buildFile = path.join(__dirname, 'build.js') 261 | 262 | for( var x in deviceList){ 263 | console.log('building :',deviceList[x]) 264 | execFileSync( 'node', [buildFile, deviceList[x]], { 265 | 266 | }) 267 | } 268 | 269 | 270 | ``` 271 | 272 | #### 修改/build/build.js 273 | > 添加设置环境变量并检查参数代码 274 | 275 | ``` 276 | var chalk = require('chalk') 277 | // 设置process.env.DEVICE_ENV参数 278 | // 没有则返回错误 279 | var device = process.argv[2] 280 | var checkDevice = require('./device-conf').checkDevice 281 | if(device){ 282 | process.env.DEVICE_ENV = device 283 | if(!checkDevice()){ 284 | return false 285 | } 286 | }else { 287 | console.log(chalk.bgRed(' 错误:缺少参数,详情请看readme.md ')) 288 | return false 289 | } 290 | ``` 291 | 292 | #### 修改/build/build.js 293 | 1. 添加一个路由(在使用中间件connect-history-api-fallback之前添加),把可调试的入口展示出来 294 | 295 | ``` 296 | // 写个小路由,打开浏览器的时候可以选一个开发路径 297 | var deviceList = require('./device-conf').deviceList || [] 298 | var sentHref = '' 299 | for(var x in deviceList){ 300 | sentHref += '点我调试终端:'+ deviceList[x].toString() +'
' 301 | } 302 | app.get('/devDeviceList', (req, res, next) => { 303 | res.send(sentHref) 304 | }) 305 | ``` 306 | 307 | 2. 修改打开的默认连接 308 | 309 | ``` 310 | opn(uri + '/devDeviceList') 311 | ``` 312 | 313 | 314 | #### 修改/config/index.js 主要修改模板入口,打包出口等 315 | 316 | ``` 317 | // see http://vuejs-templates.github.io/webpack for documentation. 318 | var path = require('path') 319 | var device = process.env.DEVICE_ENV || 'undefined' 320 | 321 | 322 | // 入口模板路径 323 | var htmlTemplate = './src/device/' + device + '/index.html' 324 | 325 | module.exports = { 326 | build: { 327 | env: require('./prod.env'), 328 | // 加入html入口 329 | htmlTemplate: htmlTemplate, 330 | index: path.resolve(__dirname, '../dist' , device , 'index.html'), 331 | assetsRoot: path.resolve(__dirname, '../dist' , device), 332 | assetsSubDirectory: 'static', 333 | // 这里的路径改成相对路径 334 | // 原来是: assetsPublicPath: '/', 335 | assetsPublicPath: '', 336 | productionSourceMap: true, 337 | // Gzip off by default as many popular static hosts such as 338 | // Surge or Netlify already gzip all static assets for you. 339 | // Before setting to `true`, make sure to: 340 | // npm install --save-dev compression-webpack-plugin 341 | productionGzip: false, 342 | productionGzipExtensions: ['js', 'css'], 343 | // Run the build command with an extra argument to 344 | // View the bundle analyzer report after build finishes: 345 | // `npm run build --report` 346 | // Set to `true` or `false` to always turn it on or off 347 | bundleAnalyzerReport: process.env.npm_config_report 348 | }, 349 | dev: { 350 | env: require('./dev.env'), 351 | port: 8080, 352 | autoOpenBrowser: true, 353 | assetsSubDirectory: 'static', 354 | assetsPublicPath: '/', 355 | proxyTable: {}, 356 | // CSS Sourcemaps off by default because relative paths are "buggy" 357 | // with this option, according to the CSS-Loader README 358 | // (https://github.com/webpack/css-loader#sourcemaps) 359 | // In our experience, they generally work as expected, 360 | // just be aware of this issue when enabling this option. 361 | cssSourceMap: false 362 | } 363 | } 364 | ``` 365 | 366 | #### 修改 /build/webpack.dev.conf.js 367 | > 主要修改了入口配置,出口配置,以及模板文件配置 368 | 369 | ``` 370 | // 获取device 371 | var device = process.env.DEVICE_ENV 372 | 373 | var utils = require('./utils') 374 | var webpack = require('webpack') 375 | var config = require('../config') 376 | var merge = require('webpack-merge') 377 | var baseWebpackConfig = require('./webpack.base.conf') 378 | var HtmlWebpackPlugin = require('html-webpack-plugin') 379 | var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 380 | 381 | // 设置设备相关信息引入 382 | var deviceList = require('./device-conf').deviceList 383 | // 其他依赖 384 | var extraPolyfill = require('./device-conf').polyfills || [] 385 | 386 | // 设置入口 387 | var entry = {} 388 | // 设置html插件模板入口和依赖 389 | var htmlPluginConf = [] 390 | for(var x in deviceList){ 391 | // 设置 入口 392 | entry[deviceList[x]] = extraPolyfill.concat( 393 | ['./build/dev-client'], 394 | './src/device/' + deviceList[x] + '/index.js' 395 | ) 396 | var _htmlPlugin = new HtmlWebpackPlugin({ 397 | filename: deviceList[x]+'/index.html', 398 | template: './src/device/' + deviceList[x] + '/index.html', 399 | chunks: [deviceList[x]] 400 | }) 401 | htmlPluginConf.push(_htmlPlugin) 402 | } 403 | 404 | 405 | 406 | 407 | // add hot-reload related code to entry chunks 408 | // 把热重载所需的代码也打包进去 409 | // Object.keys(baseWebpackConfig.entry).forEach(function (name) { 410 | // baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) 411 | // }) 412 | 413 | // 删除的entry和output 414 | try { 415 | delete baseWebpackConfig.entry 416 | }catch (e){ 417 | console.log(e) 418 | } 419 | try{ 420 | delete baseWebpackConfig.output 421 | }catch (e){ 422 | console.log(e) 423 | } 424 | 425 | module.exports = merge(baseWebpackConfig, { 426 | // 设置入口 427 | entry: entry, 428 | // 设置出口 429 | output: { 430 | path: '/', 431 | filename: '[name].js', 432 | publicPath: config.dev.assetsPublicPath 433 | }, 434 | module: { 435 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }) 436 | }, 437 | // cheap-module-eval-source-map is faster for development 438 | devtool: '#cheap-module-eval-source-map', 439 | plugins: [ 440 | new webpack.DefinePlugin({ 441 | 'process.env': config.dev.env 442 | }), 443 | // https://github.com/glenjamin/webpack-hot-middleware#installation--usage 444 | new webpack.HotModuleReplacementPlugin(), 445 | new webpack.NoEmitOnErrorsPlugin(), 446 | // https://github.com/ampedandwired/html-webpack-plugin 447 | // new HtmlWebpackPlugin({ 448 | // filename: 'index.html', 449 | // template: config.dev.htmlTemplate, 450 | // inject: true 451 | // }), 452 | new FriendlyErrorsPlugin() 453 | ].concat(htmlPluginConf) 454 | }) 455 | 456 | ``` 457 | #### 修改 /build/webpack.prod.conf.js 458 | > 主要修改了入口配置,出口配置,以及模板文件配置 459 | 460 | ``` 461 | var path = require('path') 462 | var utils = require('./utils') 463 | var webpack = require('webpack') 464 | var config = require('../config') 465 | var merge = require('webpack-merge') 466 | var baseWebpackConfig = require('./webpack.base.conf') 467 | var CopyWebpackPlugin = require('copy-webpack-plugin') 468 | var HtmlWebpackPlugin = require('html-webpack-plugin') 469 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 470 | var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') 471 | 472 | var env = process.env.NODE_ENV === 'testing' 473 | ? require('../config/test.env') 474 | : config.build.env 475 | 476 | // 设置device相关变量 477 | var device = process.env.DEVICE_ENV 478 | //设置入口 479 | var extraPolyFill = require('./device-conf').polyfills ||[] 480 | var entry = { 481 | index: extraPolyFill.concat('./src/device/' + device + '/index.js') 482 | } 483 | 484 | console.log(config.build.htmlTemplate) 485 | var webpackConfig = merge(baseWebpackConfig, { 486 | module: { 487 | rules: utils.styleLoaders({ 488 | sourceMap: config.build.productionSourceMap, 489 | extract: true 490 | }) 491 | }, 492 | // 写入prod的入口 493 | entry: entry, 494 | devtool: config.build.productionSourceMap ? '#source-map' : false, 495 | output: { 496 | path: config.build.assetsRoot, 497 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 498 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 499 | }, 500 | plugins: [ 501 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 502 | new webpack.DefinePlugin({ 503 | 'process.env': env 504 | }), 505 | new webpack.optimize.UglifyJsPlugin({ 506 | compress: { 507 | warnings: false 508 | }, 509 | sourceMap: true 510 | }), 511 | // extract css into its own file 512 | new ExtractTextPlugin({ 513 | filename: utils.assetsPath('css/[name].[contenthash].css') 514 | }), 515 | // Compress extracted CSS. We are using this plugin so that possible 516 | // duplicated CSS from different components can be deduped. 517 | new OptimizeCSSPlugin({ 518 | cssProcessorOptions: { 519 | safe: true 520 | } 521 | }), 522 | // generate dist pc.html with correct asset hash for caching. 523 | // you can customize output by editing /pc.html 524 | // see https://github.com/ampedandwired/html-webpack-plugin 525 | new HtmlWebpackPlugin({ 526 | filename: process.env.NODE_ENV === 'testing' 527 | ? 'index.html' 528 | : config.build.index, 529 | // template: 'index.html', 530 | template: config.build.htmlTemplate, 531 | inject: true, 532 | minify: { 533 | removeComments: true, 534 | collapseWhitespace: true, 535 | removeAttributeQuotes: true 536 | // more options: 537 | // https://github.com/kangax/html-minifier#options-quick-reference 538 | }, 539 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 540 | chunksSortMode: 'dependency' 541 | }), 542 | // split vendor js into its own file 543 | new webpack.optimize.CommonsChunkPlugin({ 544 | name: 'vendor', 545 | minChunks: function (module, count) { 546 | // any required modules inside node_modules are extracted to vendor 547 | return ( 548 | module.resource && 549 | /\.js$/.test(module.resource) && 550 | module.resource.indexOf( 551 | path.join(__dirname, '../node_modules') 552 | ) === 0 553 | ) 554 | } 555 | }), 556 | // extract webpack runtime and module manifest to its own file in order to 557 | // prevent vendor hash from being updated whenever app bundle is updated 558 | new webpack.optimize.CommonsChunkPlugin({ 559 | name: 'manifest', 560 | chunks: ['vendor'] 561 | }), 562 | // copy custom static assets 563 | new CopyWebpackPlugin([ 564 | { 565 | from: path.resolve(__dirname, '../static'), 566 | to: config.build.assetsSubDirectory, 567 | ignore: ['.*'] 568 | } 569 | ]) 570 | ] 571 | }) 572 | 573 | if (config.build.productionGzip) { 574 | var CompressionWebpackPlugin = require('compression-webpack-plugin') 575 | 576 | webpackConfig.plugins.push( 577 | new CompressionWebpackPlugin({ 578 | asset: '[path].gz[query]', 579 | algorithm: 'gzip', 580 | test: new RegExp( 581 | '\\.(' + 582 | config.build.productionGzipExtensions.join('|') + 583 | ')$' 584 | ), 585 | threshold: 10240, 586 | minRatio: 0.8 587 | }) 588 | ) 589 | } 590 | 591 | if (config.build.bundleAnalyzerReport) { 592 | var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 593 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 594 | } 595 | 596 | module.exports = webpackConfig 597 | 598 | ``` 599 | 600 | ## 六、整个开发环境的目录注释 601 | 602 | ``` 603 | │ .babelrc 604 | │ .editorconfig 605 | │ .eslintignore 606 | │ .eslintrc.js 607 | │ .gitignore 608 | │ .postcssrc.js 609 | │ index.html 610 | │ npm-debug.log 611 | │ package.json 612 | │ README.md 613 | │ tree.txt 614 | │ 615 | ├─build # 这里是打包工具相关的 616 | │ build-all.js # 通过打包所有端的代码 617 | │ build.js # 这里设定进程的环境变量 618 | │ check-versions.js 619 | │ dev-client.js 620 | │ dev-server.js # 这里也需要对进程的环境变量进行设定 621 | │ device-conf.js # 这里面有关于多端开发、打包的相关设定 622 | │ utils.js 623 | │ vue-loader.conf.js 624 | │ webpack.base.conf.js # 修改了入口、出口等 625 | │ webpack.dev.conf.js # 修改了入口、出口等 626 | │ webpack.prod.conf.js # 修改了入口出口等 627 | │ webpack.test.conf.js # 测试相关还未完善 628 | │ 629 | ├─config 630 | │ dev.env.js 631 | │ index.js # 打包的入口和出口 632 | │ prod.env.js 633 | │ test.env.js 634 | │ 635 | ├─dist # 最后打包的目录 分端储存 636 | │ ├─app 637 | │ │ │ index.html 638 | │ │ │ 639 | │ │ └─static 640 | │ │ └─js 641 | │ │ index.0142f89e3523b3b0d16b.js 642 | │ │ index.0142f89e3523b3b0d16b.js.map 643 | │ │ manifest.57f6691c595e842abc95.js 644 | │ │ manifest.57f6691c595e842abc95.js.map 645 | │ │ vendor.cce790f63359fc27fa7d.js 646 | │ │ vendor.cce790f63359fc27fa7d.js.map 647 | │ │ 648 | │ ├─mobile 649 | │ │ │ index.html 650 | │ │ │ 651 | │ │ └─static 652 | │ │ └─js 653 | │ │ index.0142f89e3523b3b0d16b.js 654 | │ │ index.0142f89e3523b3b0d16b.js.map 655 | │ │ manifest.57f6691c595e842abc95.js 656 | │ │ manifest.57f6691c595e842abc95.js.map 657 | │ │ vendor.cce790f63359fc27fa7d.js 658 | │ │ vendor.cce790f63359fc27fa7d.js.map 659 | │ │ 660 | │ └─pc 661 | │ │ index.html 662 | │ │ 663 | │ └─static 664 | │ ├─css 665 | │ │ index.1e809171f3a961de951e3c8e6644435f.css 666 | │ │ index.1e809171f3a961de951e3c8e6644435f.css.map 667 | │ │ 668 | │ └─js 669 | │ 0.f3e74a76d92b3f6ca5ec.js 670 | │ 0.f3e74a76d92b3f6ca5ec.js.map 671 | │ 1.fb471d3425df8c16ac54.js 672 | │ 1.fb471d3425df8c16ac54.js.map 673 | │ index.a2ba631673923f812cf1.js 674 | │ index.a2ba631673923f812cf1.js.map 675 | │ manifest.ab6461111db19541d04b.js 676 | │ manifest.ab6461111db19541d04b.js.map 677 | │ vendor.aeee805b1efff3748018.js 678 | │ vendor.aeee805b1efff3748018.js.map 679 | │ 680 | ├─images # 这个放点文档写文档用的图片 681 | ├─sever # 这里写点服务端程序,用于测试等 682 | │ prod-view-server.js 683 | │ 684 | ├─src # 源文件目录 685 | │ │ config.js 686 | │ │ 687 | │ ├─api # 多端共用的 api 688 | │ │ device-root.js 689 | │ │ middleware.js 690 | │ │ 691 | │ ├─assets # 多端共用的 资源 692 | │ │ logo.png 693 | │ │ 694 | │ ├─components # 多端共用的 组件 695 | │ │ RootCommonComponent.vue 696 | │ │ 697 | │ └─device # 设备入口 698 | │ ├─app # 混合开发的放这里了,也可以分 ios 和 安卓 699 | │ │ index.html # app专用的html模板,打包好的东西会自动注入 700 | │ │ index.js # app的入口文件 701 | │ │ 702 | │ ├─mobile # 这里放移动端的页面 ,下面的 index文件作用类似其他端 703 | │ │ index.html 704 | │ │ index.js 705 | │ │ 706 | │ └─pc # 这个目录下的都是pc端使用的,当然其他端要用也是可以的,哈哈 707 | │ │ App.vue # 入口组件 708 | │ │ index.html # 模板文件 709 | │ │ index.js # 入口文件 710 | │ │ 711 | │ ├─api # 分离开接口 712 | │ │ home.js # home这个模块用的接口 713 | │ │ middleware.js # 一些公用的中间件 714 | │ │ 715 | │ ├─assets # 资源 716 | │ ├─components # 组件 717 | │ │ ├─commonComponents # 公共组件 718 | │ │ │ Header.vue 719 | │ │ │ 720 | │ │ ├─Home # home这个模块用的组件 721 | │ │ │ Body.vue 722 | │ │ │ Index.vue 723 | │ │ │ 724 | │ │ └─Page404 # 404这个模块用的组件 725 | │ │ Index.vue 726 | │ │ 727 | │ ├─router # 路由 728 | │ │ index.js 729 | │ │ 730 | │ ├─store # vuex 的store 731 | │ │ │ index.js # 根级别的store + 模块组装 732 | │ │ │ 733 | │ │ └─modules # store 模块 734 | │ │ home.js # home这个模块使用的store 735 | │ │ 736 | │ └─types # 放类型名称 737 | │ home.js # home这个模块使用的 types 738 | │ root.js # 公用的types 739 | │ 740 | ├─static 741 | │ .gitkeep 742 | │ 743 | └─test # 测试相关 TODO 744 | 745 | ``` 746 | 747 | 748 | 749 | --------------------------------------------------------------------------------