Repository: lincenying/vue2-multiple-entry Branch: master Commit: 62432a5a2322 Files: 53 Total size: 32.9 KB Directory structure: gitextract_x_z8bi1j/ ├── .browserslistrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .prettierignore ├── .prettierrc ├── README.md ├── babel.config.js ├── entris.js ├── jsconfig.json ├── package.json ├── postcss.config.js ├── public/ │ ├── index.html │ ├── robots.txt │ └── static/ │ └── manifest.json ├── src/ │ ├── api/ │ │ ├── config.js │ │ └── index.js │ ├── assets/ │ │ └── scss/ │ │ ├── _reset.scss │ │ └── style.scss │ ├── components/ │ │ ├── module-1.vue │ │ ├── module-2.vue │ │ ├── module-3.vue │ │ ├── module-view.vue │ │ └── nav-component.vue │ ├── modules/ │ │ ├── index/ │ │ │ ├── app.vue │ │ │ └── index.js │ │ ├── module2/ │ │ │ ├── app.vue │ │ │ └── index.js │ │ ├── module3/ │ │ │ ├── app.vue │ │ │ └── index.js │ │ ├── router/ │ │ │ ├── app.vue │ │ │ ├── components/ │ │ │ │ ├── lists.vue │ │ │ │ └── view.vue │ │ │ ├── index.js │ │ │ ├── pages/ │ │ │ │ ├── index.vue │ │ │ │ └── view.vue │ │ │ └── router/ │ │ │ └── index.js │ │ ├── view/ │ │ │ ├── app.vue │ │ │ └── index.js │ │ └── vuex/ │ │ ├── app.vue │ │ ├── components/ │ │ │ ├── lists.vue │ │ │ └── view.vue │ │ ├── index.js │ │ ├── pages/ │ │ │ ├── index.vue │ │ │ └── view.vue │ │ ├── router/ │ │ │ └── index.js │ │ └── store/ │ │ ├── index.js │ │ └── modules/ │ │ ├── topic.js │ │ └── topics.js │ ├── polyfill/ │ │ └── index.js │ └── utils/ │ └── index.js └── vue.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .browserslistrc ================================================ > 1% last 2 versions ================================================ FILE: .editorconfig ================================================ root = true [*] charset = utf-8 indent_style = space indent_size = 4 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true ================================================ FILE: .eslintignore ================================================ build/*.js config/*.js public/static/**/*.js dist/**/*.js dist/**/*.css ================================================ FILE: .eslintrc.json ================================================ { "extends": ["lcy-vue"], "rules": { "no-irregular-whitespace": 0 } } ================================================ FILE: .gitignore ================================================ .DS_Store .idea/ node_modules /dist # local env files .env.local .env.*.local # Log files npm-debug.log* yarn-debug.log* yarn-error.log* # Editor directories and files .idea .vscode *.suo *.ntvs* *.njsproj *.sln *.sw? *-port.txt package-lock.json admin.lock yarn-error.log server/config/secret.js server/config/mpapp.js server/config/shihua.js ================================================ FILE: .prettierignore ================================================ package.json ================================================ FILE: .prettierrc ================================================ { "printWidth": 150, "semi": false, "tabWidth": 4, "singleQuote": true, "trailingComma": "none", "arrowParens": "avoid", "htmlWhitespaceSensitivity": "css" } ================================================ FILE: README.md ================================================ # vue2 多页配置实例 基于`vue2 + vue-router + vuex`而组成的多页配置实例 多页实例中包含 纯单页, 基于 vue-router 的单页, 基于 vue-router + vuex 的单页, 多种模式自由组合 在 module 文件夹中只留一个模块, 就变成 纯`SPA` 所有模块均带修改head里的title, 所有模块也都包含`ajax`例子, 任何不带`ajax`的例子都是耍流氓 vuex模块带有懒加载 DEMO: http://old.mmxiaowu.com/vue2-multiple-entry/index fork demo ```bash $ git clone // 安装依赖 $ yarn // 生产模式 $ yarn build // 开发模式 $ yarn serve // eslint 检测 $ yarn lint ``` 访问: http://localhost:8086/index # 目录结构 - /dist/ = webpack编译后生成文件目录 - /src/api/ = axios配置目录 - /src/assets/ = 静态文件目录 - /src/components/ = 组件目录 - /src/modules/ = 多页面模块, 每个模块一个文件夹 - /src/modules/router   = 多页面模块之带路由模块的例子 - /src/modules/vuex   = 多页面模块之带路由和vuex的模块的例子 - /utils/ = 一些有用的插件 ================================================ FILE: babel.config.js ================================================ module.exports = { presets: ['@vue/app'] } ================================================ FILE: entris.js ================================================ const fs = require('fs') const path = require('path') const entryPath = path.resolve(__dirname, './src/modules') const entris = fs.readdirSync(entryPath).reduce(function (o, dirname) { o[dirname] = path.join(entryPath, dirname) return o }, {}) module.exports = entris ================================================ FILE: jsconfig.json ================================================ { "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["src/*"] }, "target": "ES6", "module": "commonjs", "allowSyntheticDefaultImports": true }, "include": ["src/**/*"], "exclude": ["node_modules"] } ================================================ FILE: package.json ================================================ { "name": "vue2-multiple-entry", "version": "2.0.2", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint" }, "dependencies": { "axios": "^0.21.1", "core-js": "^3.8.3", "nprogress": "^0.2.0", "store2": "^2.12.0", "toastr": "^2.1.4", "vue": "^2.6.12", "vue-meta": "^2.4.0", "vue-router": "^3.5.1", "vueg": "^1.4.5", "vuex": "^3.6.2", "vuex-router-sync": "^5.0.0" }, "devDependencies": { "@vue/cli-plugin-babel": "^4.5.11", "@vue/cli-plugin-eslint": "^4.5.11", "@vue/cli-service": "^4.5.11", "@vue/eslint-config-prettier": "^6.0.0", "babel-eslint": "^10.1.0", "caniuse-lite": "^1.0.30001183", "eslint": "^7.19.0", "eslint-config-lcy-vue": "^3.1.1", "eslint-plugin-babel": "^5.3.1", "eslint-plugin-import": "^2.22.1", "eslint-plugin-prettier": "^3.3.1", "eslint-plugin-vue": "^7.5.0", "lint-staged": "^10.5.3", "node-sass": "^5.0.0", "prettier": "^2.2.1", "sass-loader": "^10.1.1", "vue-template-compiler": "^2.6.12" }, "gitHooks": { "pre-commit": "lint-staged" }, "lint-staged": { "*.{js,vue}": [ "vue-cli-service lint" ] } } ================================================ FILE: postcss.config.js ================================================ module.exports = { plugins: { autoprefixer: {} } } ================================================ FILE: public/index.html ================================================ vue2-multiple-entry
================================================ FILE: public/robots.txt ================================================ User-agent: * Disallow: ================================================ FILE: public/static/manifest.json ================================================ { "name": "vue2-multiple-entry", "short_name": "vue2-multiple-entry", "icons": [ { "src": "/static/img/icons/android-chrome-48x48.png", "sizes": "48x48", "type": "image/png" }, { "src": "/static/img/icons/android-chrome-72x72.png", "sizes": "72x72", "type": "image/png" }, { "src": "/static/img/icons/android-chrome-96x96.png", "sizes": "96x96", "type": "image/png" }, { "src": "/static/img/icons/msapplication-icon-144x144.png", "sizes": "144x144", "type": "image/png" }, { "src": "/static/img/icons/android-chrome-168x168.png", "sizes": "168x168", "type": "image/png" }, { "src": "/static/img/icons/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/static/img/icons/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" } ], "start_url": "/", "background_color": "#000000", "display": "standalone", "theme_color": "#2874f0" } ================================================ FILE: src/api/config.js ================================================ module.exports = { api: 'https://cnodejs.org/api/v1/', timeout: 30000 } ================================================ FILE: src/api/index.js ================================================ import config from '@/api/config' import { setMessage } from '@/utils' import axios from 'axios' import NProgress from 'nprogress' import qs from 'qs' axios.interceptors.request.use( config => { NProgress.start() return config }, error => Promise.reject(error) ) axios.interceptors.response.use( response => response, error => Promise.resolve(error.response) ) function checkStatus(response) { NProgress.done() if (response.status === 200 || response.status === 304) { return response.data } return { success: false, message: response.statusText, data: response.statusText } } function checkCode(res) { if (!res.success) { setMessage(res.message || res.data) } return res } export default { post(url, data) { return axios({ method: 'post', url: config.api + url, data: qs.stringify(data), timeout: config.timeout, headers: { 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' } }) .then(checkStatus) .then(checkCode) }, get(url, params) { return axios({ method: 'get', url: config.api + url, params, timeout: config.timeout, headers: { 'X-Requested-With': 'XMLHttpRequest' } }) .then(checkStatus) .then(checkCode) } } ================================================ FILE: src/assets/scss/_reset.scss ================================================ html { overflow-x: hidden; overflow-y: scroll; background: #ebf0f0; overflow-anchor: none; } /* 清除内外边距 */ body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, /* structural elements 结构元素 */ dl, dt, dd, ul, ol, li, /* list elements 列表元素 */ pre, /* text formatting elements 文本格式元素 */ fieldset, button, input, textarea, th, td { margin: 0; padding: 0; } /* 设置默认字体 */ body, button, input, select, textarea { color: #333; outline: none; font-family: -apple-system-font, "Helvetica Neue", Tahoma, "PingFang SC", "lantinghei sc", "Microsoft Yahei", sans-serif; font-size: 16px; line-height: 1.6; font-feature-settings: 'kern' 1; -webkit-font-smoothing: antialiased; } h1 { font-size: 20px; /* 18px / 12px = 1.5 */ } h2 { font-size: 18px; } h3 { font-size: 16px; } h4, h5, h6 { font-size: 100%; } address, cite, dfn, em, var { font-style: normal; } /* 统一等宽字体 */ small { font-size: 12px; } /* 小于 12px 的中文很难阅读,让 small 正常化 */ /* 重置列表元素 */ ul, ol { list-style: none; } /* 重置文本格式元素 */ a { text-decoration: none; -webkit-tap-highlight-color: rgba(255, 255, 255, 0); } a:hover { text-decoration: none; } abbr[title], acronym[title] { /* 注:1.ie6 不支持 abbr; 2.这里用了属性选择符,ie6 下无效果 */ border-bottom: 1px dotted; cursor: help; } q:before, q:after { content: ''; } /* 重置表单元素 */ legend { color: #000; } /* for ie6 */ fieldset, img { border: none; } /* 注:optgroup 无法扶正 */ button, input, select, textarea { font-size: 100%; /* 使得表单元素在 ie 下能继承字体大小 */ } /* 重置表格元素 */ table { border-collapse: collapse; border-spacing: 0; } /* 重置 hr */ hr { border: none; height: 1px; } article, aside, canvas, figure, figure img, figcaption, hgroup, footer, header, nav, section, audio, video { position: relative; display: block; } nav ul, nav ol { list-style: none; } .hidden { display: none; } .clearfix { *zoom: 1; } .clearfix:before, .clearfix:after { display: table; content: ""; } .clearfix:after { clear: both; } ================================================ FILE: src/assets/scss/style.scss ================================================ @import '_reset.scss'; #vueg-background { padding-top: 80px; box-sizing: border-box; background: none !important; } .g-doc { display: flex; flex-direction: column; min-height: 100vh; } .nav-wrap { flex: 0 0 60px; margin-bottom: 20px; background: #54d9e0; .nav-block { display: flex; align-items: center; width: 1200px; height: 60px; margin: 0 auto; li { flex: 0 0 auto; list-style: none; width: 120px; text-align: center; a { font-size: 16px; color: #fff; } &.current { a { color: red; } } &.logo { width: 109px; margin-right: 30px; img { width: 109px; height: 31px; } } } } } .body { flex: 1 1 auto; width: 1200px; margin: 0 auto 20px; padding: 20px; background: #fff; border-radius: 5px; box-shadow: 0 0 10px #eee; .header { position: relative; padding-bottom: 15px; text-align: center; font-size: 16px; font-weight: 800; border-bottom: 1px solid #eee; a { display: inline-block; position: absolute; right: 0; top: 0; font-weight: 400; font-size: 14px; line-height: 22px; } } .topics { margin-top: 20px; li + li { margin-top: 10px; } } .content { margin-top: 20px; .markdown-text { word-break: break-all; } img { max-width: 100%; } } .pages { margin-top: 20px; padding-top: 20px; text-align: center; border-top: 1px solid #eee; a { display: inline-block; & + a { margin-left: 20px; } } } } ================================================ FILE: src/components/module-1.vue ================================================ ================================================ FILE: src/components/module-2.vue ================================================ ================================================ FILE: src/components/module-3.vue ================================================ ================================================ FILE: src/components/module-view.vue ================================================ ================================================ FILE: src/components/nav-component.vue ================================================ ================================================ FILE: src/modules/index/app.vue ================================================ ================================================ FILE: src/modules/index/index.js ================================================ import '@/polyfill' import Vue from 'vue' import Meta from 'vue-meta' import App from './app.vue' Vue.use(Meta) const app = new Vue({ ...App }) app.$mount('#app') ================================================ FILE: src/modules/module2/app.vue ================================================ ================================================ FILE: src/modules/module2/index.js ================================================ import '@/polyfill' import Vue from 'vue' import Meta from 'vue-meta' import App from './app.vue' Vue.use(Meta) const app = new Vue({ ...App }) app.$mount('#app') ================================================ FILE: src/modules/module3/app.vue ================================================ ================================================ FILE: src/modules/module3/index.js ================================================ import '@/polyfill' import Vue from 'vue' import Meta from 'vue-meta' import App from './app.vue' Vue.use(Meta) const app = new Vue({ ...App }) app.$mount('#app') ================================================ FILE: src/modules/router/app.vue ================================================ ================================================ FILE: src/modules/router/components/lists.vue ================================================ ================================================ FILE: src/modules/router/components/view.vue ================================================ ================================================ FILE: src/modules/router/index.js ================================================ import '@/polyfill' import Vue from 'vue' import Meta from 'vue-meta' // import vueg from 'vueg' import router from './router' import App from './app.vue' Vue.use(Meta) // Vue.use(vueg, router, { // shadow: false, // forwardAnim: 'fadeInRight' // }) const app = new Vue({ router, ...App }) app.$mount('#app') ================================================ FILE: src/modules/router/pages/index.vue ================================================ ================================================ FILE: src/modules/router/pages/view.vue ================================================ ================================================ FILE: src/modules/router/router/index.js ================================================ /* eslint-disable no-inline-comments */ import Vue from 'vue' import VueRouter from 'vue-router' // 正常打包 // import index from '../pages/index.vue' // import view from '../pages/view.vue' // 懒加载 // const index = resolve => require(['../pages/index.vue'], resolve) // const view = resolve => require(['../pages/view.vue'], resolve) // 懒加载 - 按组 // const index = r => require.ensure([], () => r(require('../pages/index.vue')), 'group-index') // const view = r => require.ensure([], () => r(require('../pages/view.vue')), 'group-view') // 懒加载 - 按组 import const index = () => import(/* webpackChunkName: "router-index" */ '../pages/index.vue') const view = () => import(/* webpackChunkName: "router-view" */ '../pages/view.vue') Vue.use(VueRouter) const router = new VueRouter({ mode: 'hash', //base: __dirname, routes: [ { name: 'index', path: '/', component: index }, { name: 'view', path: '/view/:id', component: view } ] }) export default router ================================================ FILE: src/modules/view/app.vue ================================================ ================================================ FILE: src/modules/view/index.js ================================================ import '@/polyfill' import Vue from 'vue' import Meta from 'vue-meta' import App from './app.vue' Vue.use(Meta) const app = new Vue({ ...App }) app.$mount('#app') ================================================ FILE: src/modules/vuex/app.vue ================================================ ================================================ FILE: src/modules/vuex/components/lists.vue ================================================ ================================================ FILE: src/modules/vuex/components/view.vue ================================================ ================================================ FILE: src/modules/vuex/index.js ================================================ import '@/polyfill' import Vue from 'vue' import Meta from 'vue-meta' // import vueg from 'vueg' import store from './store' import router from './router' import { sync } from 'vuex-router-sync' import App from './app.vue' Vue.use(Meta) // Vue.use(vueg, router, { // // duration: '10', // shadow: false, // forwardAnim: 'fadeInRight' // }) sync(store, router) router.beforeEach((route, redirect, next) => { window.scrollTo(0, 0) next() }) const app = new Vue({ router, store, ...App }) app.$mount('#app') ================================================ FILE: src/modules/vuex/pages/index.vue ================================================ ================================================ FILE: src/modules/vuex/pages/view.vue ================================================ ================================================ FILE: src/modules/vuex/router/index.js ================================================ /* eslint-disable no-inline-comments */ import Vue from 'vue' import VueRouter from 'vue-router' // 正常打包 // import index from '../pages/index.vue' // import view from '../pages/view.vue' // 懒加载 // const index = resolve => require(['../pages/index.vue'], resolve) // const view = resolve => require(['../pages/view.vue'], resolve) // 懒加载 - 按组 // const index = r => require.ensure([], () => r(require('../pages/index.vue')), 'group-index') // const view = r => require.ensure([], () => r(require('../pages/view.vue')), 'group-view') // 懒加载 - 按组 import const index = () => import(/* webpackChunkName: "vuex-index" */ '../pages/index.vue') const view = () => import(/* webpackChunkName: "vuex-view" */ '../pages/view.vue') Vue.use(VueRouter) const router = new VueRouter({ mode: 'hash', //base: __dirname, routes: [ { name: 'index', path: '/', component: index }, { name: 'view', path: '/view/:id', component: view } ] }) export default router ================================================ FILE: src/modules/vuex/store/index.js ================================================ import Vue from 'vue' import Vuex from 'vuex' import topics from './modules/topics' import topic from './modules/topic' Vue.use(Vuex) export default new Vuex.Store({ modules: { topics, topic } }) ================================================ FILE: src/modules/vuex/store/modules/topic.js ================================================ import api from '@/api' const state = { item: { data: {}, path: '' } } const actions = { async ['getTopic']( { commit, state, rootState: { route: { fullPath } } }, config ) { const path = fullPath if (state.item.data.id && path === state.item.path) { return } const { data, success } = await api.get(`topic/${config.id}`) if (data && success) { commit('receiveTopic', { data, path }) } } } const mutations = { ['receiveTopic'](state, { data, path }) { state.item = { data, path } } } const getters = { ['getTopic'](state) { return state.item } } export default { namespaced: true, state, actions, mutations, getters } ================================================ FILE: src/modules/vuex/store/modules/topics.js ================================================ import api from '@/api' const state = { lists: { data: [], page: 1, path: '' } } const actions = { async ['getTopics']( { commit, state, rootState: { route: { fullPath } } }, config ) { const path = fullPath if (state.lists.data.length > 0 && path === state.lists.path && config.page === 1) { return } const { data, success } = await api.get('topics', config) if (data && success) { commit('receiveTopics', { ...config, data, path }) } } } const mutations = { ['receiveTopics'](state, { data, page, path }) { let list if (page === 1) { list = [].concat(data) } else { list = state.lists.data.concat(data) } state.lists = { data: list, page, path } } } const getters = { ['getTopics'](state) { return state.lists } } export default { namespaced: true, state, actions, mutations, getters } ================================================ FILE: src/polyfill/index.js ================================================ import 'core-js/features/promise' import 'core-js/features/array/find' import 'core-js/features/array/find-index' import 'core-js/features/array/includes' ================================================ FILE: src/utils/index.js ================================================ import toastr from 'toastr' toastr.options.positionClass = 'toast-top-center' function pluralize(time, label) { return time + label } export const setMessage = config => { let content, type if (typeof config === 'string') { content = config type = 'error' } else { content = config.content type = config.type } toastr[type](content) } export const strlen = str => { let charCode = -1 const len = str.length let realLength = 0 for (let i = 0; i < len; i++) { charCode = str.charCodeAt(i) if (charCode >= 0 && charCode <= 128) realLength += 1 else realLength += 2 } return realLength } export function timeAgo(time) { const preg = /^[\d]+$/ const timestamp = preg.test(time) if (!timestamp) { const tmp = Date.parse(time) time = tmp / 1000 } const between = Date.now() / 1000 - Number(time) if (between < 60) { return '刚刚' } else if (between < 3600) { return pluralize(parseInt(between / 60, 10), ' 分钟前') } else if (between < 86400) { return pluralize(parseInt(between / 3600, 10), ' 小时前') } return pluralize(parseInt(between / 86400, 10), ' 天前') } export const timeYmd = timestamp => { var time = new Date(timestamp * 1000) var year = time.getFullYear() var month = time.getMonth() + 1 var date = time.getDate() return year + '-' + (month < 10 ? '0' + month : month) + '-' + (date < 10 ? '0' + date : date) } ================================================ FILE: vue.config.js ================================================ /* eslint-disable no-inline-comments */ const entris = require('./entris') const pages = {} Object.keys(entris).forEach(item => { pages[item] = { // page 的入口 entry: 'src/modules/' + item + '/index.js', // 模板来源 template: 'public/index.html', // 在 dist/index.html 的输出 filename: item + '.html' } }) module.exports = { publicPath: '/', // publicPath: '/vue2-multiple-entry/', pages, configureWebpack: { devtool: 'source-map', performance: { hints: 'warning', maxAssetSize: 30000000, // 单位为字节 maxEntrypointSize: 50000000, // 单位为字节 assetFilter(assetFilename) { // 提供资源文件名的断言函数 return assetFilename.endsWith('.css') || assetFilename.endsWith('.js') } } }, chainWebpack: config => { config.module .rule('vue') .use('vue-loader') .tap(options => { options.compilerOptions.preserveWhitespace = true return options }) config.module.rule('eslint').uses.clear() config.module.rule('eslint').clear() }, css: { loaderOptions: {} }, pluginOptions: {}, devServer: { port: 8086, host: '0.0.0.0', hot: true, disableHostCheck: true, proxy: { '/api': { target: 'http://localhost:4000', changeOrigin: true, pathRewrite: { '^/api': '/api' } } }, historyApiFallback: { rewrites: [ // shows views/landing.html as the landing page { from: /^\/$/, to: '/index.html' }, // shows views/subpage.html for all routes starting with /subpage { from: /^\/module2/, to: '/module2.html' }, { from: /^\/module3/, to: '/module3.html' }, { from: /^\/router/, to: '/router.html' }, { from: /^\/vuex/, to: '/vuex.html' }, { from: /^\/view/, to: '/view.html' } ] } } }