mirror of
https://github.com/vuejs/vue-cli.git
synced 2026-03-06 22:39:02 -06:00
feat: router & vuex
This commit is contained in:
@@ -6,7 +6,11 @@
|
||||
],
|
||||
"scripts": {
|
||||
"test": "jest --env node",
|
||||
"lint": "eslint --fix packages/**/*.js packages/**/bin/* test/**/*.js"
|
||||
"posttest": "yarn clean",
|
||||
"lint": "eslint --fix packages/**/*.js packages/**/bin/* test/**/*.js",
|
||||
"clean": "node scripts/cleanTestDir.js",
|
||||
"sync": "node scripts/syncDeps.js",
|
||||
"boot": "node scripts/bootstrap.js"
|
||||
},
|
||||
"gitHooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module.exports = (generatorAPI, options) => {
|
||||
generatorAPI.render('./template')
|
||||
generatorAPI.extendPackage({
|
||||
module.exports = (api, options) => {
|
||||
api.render('./template')
|
||||
api.extendPackage({
|
||||
scripts: {
|
||||
'serve': 'vue-cli-service serve' + (
|
||||
// only auto open browser on MacOS where applescript
|
||||
@@ -28,4 +28,20 @@ module.exports = (generatorAPI, options) => {
|
||||
'not ie <= 8'
|
||||
]
|
||||
})
|
||||
|
||||
if (options.router) {
|
||||
api.extendPackage({
|
||||
dependencies: {
|
||||
'vue-router': '^3.0.1'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (options.vuex) {
|
||||
api.extendPackage({
|
||||
dependencies: {
|
||||
vuex: '^3.0.1'
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%_ if (!options.router) { _%>
|
||||
<template>
|
||||
<div id="app">
|
||||
<img src="./assets/logo.png">
|
||||
@@ -15,6 +16,17 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<%_ } else { _%>
|
||||
<template>
|
||||
<div id="app">
|
||||
<div id="nav">
|
||||
<router-link to="/">Home</router-link> |
|
||||
<router-link to="/about">About</router-link>
|
||||
</div>
|
||||
<router-view/>
|
||||
</div>
|
||||
</template>
|
||||
<%_ } _%>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
@@ -23,6 +35,23 @@ export default {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
<%_ if (!options.router) { _%>
|
||||
margin-top: 60px;
|
||||
<%_ } _%>
|
||||
}
|
||||
|
||||
<%_ if (options.router) { _%>
|
||||
#nav {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
#nav a {
|
||||
font-weight: bold;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
#nav a.router-link-exact-active {
|
||||
color: #42b983;
|
||||
}
|
||||
<%_ } _%>
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
<%_ if (options.router) { _%>
|
||||
import router from './router'
|
||||
<%_ } _%>
|
||||
<%_ if (options.vuex) { _%>
|
||||
import store from './store'
|
||||
<%_ } _%>
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
new Vue({
|
||||
<%_ if (options.router) { _%>
|
||||
router,
|
||||
<%_ } _%>
|
||||
<%_ if (options.vuex) { _%>
|
||||
store,
|
||||
<%_ } _%>
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
|
||||
23
packages/@vue/cli-service/generator/template/src/router.js
Normal file
23
packages/@vue/cli-service/generator/template/src/router.js
Normal file
@@ -0,0 +1,23 @@
|
||||
<%_ if (options.router) { _%>
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
import Home from './views/Home.vue'
|
||||
import About from './views/About.vue'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
export default new Router({
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: Home
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'about',
|
||||
component: About
|
||||
}
|
||||
]
|
||||
})
|
||||
<%_ } _%>
|
||||
18
packages/@vue/cli-service/generator/template/src/store.js
Normal file
18
packages/@vue/cli-service/generator/template/src/store.js
Normal file
@@ -0,0 +1,18 @@
|
||||
<%_ if (options.vuex) { _%>
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
export default new Vuex.Store({
|
||||
state: {
|
||||
|
||||
},
|
||||
mutations: {
|
||||
|
||||
},
|
||||
actions: {
|
||||
|
||||
}
|
||||
})
|
||||
<%_ } _%>
|
||||
@@ -0,0 +1,7 @@
|
||||
<%_ if (options.router) { _%>
|
||||
<template>
|
||||
<div class="about">
|
||||
<h1>This is an about page</h1>
|
||||
</div>
|
||||
</template>
|
||||
<%_ } _%>
|
||||
@@ -0,0 +1,20 @@
|
||||
<%_ if (options.router) { _%>
|
||||
<template>
|
||||
<div class="home">
|
||||
<img src="../assets/logo.png">
|
||||
<HelloWorld msg="Welcome to Your Vue.js App"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// @ is an alias to /src
|
||||
import HelloWorld from '@/components/HelloWorld.vue'
|
||||
|
||||
export default {
|
||||
name: 'home',
|
||||
components: {
|
||||
HelloWorld
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<%_ } _%>
|
||||
@@ -58,7 +58,9 @@
|
||||
"yorkie": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vue": "^2.5.13"
|
||||
"vue": "^2.5.13",
|
||||
"vue-router": "^3.0.1",
|
||||
"vuex": "^3.0.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -59,19 +59,12 @@ test('save options (merge)', () => {
|
||||
bar: { b: 2 }
|
||||
}
|
||||
})
|
||||
|
||||
// deep merge
|
||||
saveOptions({
|
||||
plugins: {
|
||||
foo: { a: 2, c: 3 },
|
||||
bar: { d: 4 }
|
||||
}
|
||||
}, true)
|
||||
expect(loadOptions()).toEqual({
|
||||
packageManager: 'yarn',
|
||||
plugins: {
|
||||
foo: { a: 2, c: 3 },
|
||||
bar: { b: 2, d: 4 }
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
test('save options (replace)', () => {
|
||||
const toSave = {
|
||||
foo: 'bar'
|
||||
}
|
||||
saveOptions(toSave, true)
|
||||
expect(loadOptions()).toEqual(toSave)
|
||||
})
|
||||
|
||||
@@ -72,7 +72,9 @@ module.exports = class Creator {
|
||||
|
||||
// inject core service
|
||||
options.plugins['@vue/cli-service'] = {
|
||||
projectName: name
|
||||
projectName: name,
|
||||
router: options.router,
|
||||
vuex: options.vuex
|
||||
}
|
||||
|
||||
const packageManager = (
|
||||
@@ -180,7 +182,7 @@ module.exports = class Creator {
|
||||
|
||||
// save options
|
||||
if (answers.mode === 'manual' && answers.save) {
|
||||
saveOptions(options)
|
||||
saveOptions(options, true /* replace */)
|
||||
}
|
||||
|
||||
debug('vue:cli-ptions')(options)
|
||||
@@ -200,7 +202,7 @@ module.exports = class Creator {
|
||||
}
|
||||
|
||||
resolveIntroPrompts () {
|
||||
const defualtFeatures = formatFeatures(defaults.plugins)
|
||||
const defualtFeatures = formatFeatures(defaults)
|
||||
const modePrompt = {
|
||||
name: 'mode',
|
||||
type: 'list',
|
||||
@@ -218,7 +220,7 @@ module.exports = class Creator {
|
||||
}
|
||||
const savedOptions = loadOptions()
|
||||
if (savedOptions.plugins) {
|
||||
const savedFeatures = formatFeatures(savedOptions.plugins)
|
||||
const savedFeatures = formatFeatures(savedOptions)
|
||||
modePrompt.choices.unshift({
|
||||
name: `Use previously saved config (${savedFeatures})`,
|
||||
value: 'saved'
|
||||
|
||||
@@ -61,7 +61,10 @@ module.exports = class GeneratorAPI {
|
||||
})
|
||||
for (const file of _files) {
|
||||
const relativePath = path.relative(fileDir, file.path)
|
||||
files[relativePath] = renderFile(file.path, data, ejsOptions)
|
||||
const content = renderFile(file.path, data, ejsOptions)
|
||||
if (Buffer.isBuffer(content) || content.trim()) {
|
||||
files[relativePath] = content
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if (isObject(fileDir)) {
|
||||
@@ -71,7 +74,10 @@ module.exports = class GeneratorAPI {
|
||||
}, additionalData)
|
||||
for (const targetPath in fileDir) {
|
||||
const sourcePath = path.resolve(baseDir, fileDir[targetPath])
|
||||
files[targetPath] = renderFile(sourcePath, data, ejsOptions)
|
||||
const content = renderFile(sourcePath, data, ejsOptions)
|
||||
if (Buffer.isBuffer(content) || content.trim()) {
|
||||
files[targetPath] = content
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if (isFunction(fileDir)) {
|
||||
|
||||
@@ -34,10 +34,16 @@ async function create (projectName, options) {
|
||||
}
|
||||
}
|
||||
|
||||
const promptModules = fs
|
||||
.readdirSync(path.resolve(__dirname, './promptModules'))
|
||||
.filter(file => file.charAt(0) !== '.' && file.charAt(0) !== '_')
|
||||
.map(file => require(`./promptModules/${file}`))
|
||||
const promptModules = [
|
||||
'babel',
|
||||
'router',
|
||||
'vuex',
|
||||
'eslint',
|
||||
'unit',
|
||||
'e2e',
|
||||
'typescript',
|
||||
'pwa'
|
||||
].map(file => require(`./promptModules/${file}`))
|
||||
|
||||
const creator = new Creator(projectName, targetDir, promptModules)
|
||||
await creator.create(options)
|
||||
|
||||
@@ -14,6 +14,8 @@ const rcPath = exports.rcPath = (
|
||||
)
|
||||
|
||||
const schema = createSchema(joi => joi.object().keys({
|
||||
router: joi.boolean(),
|
||||
vuex: joi.boolean(),
|
||||
useTaobaoRegistry: joi.any().only([true, false, null]),
|
||||
packageManager: joi.string().only(['yarn', 'npm']),
|
||||
plugins: joi.object().required()
|
||||
@@ -22,6 +24,8 @@ const schema = createSchema(joi => joi.object().keys({
|
||||
exports.validate = options => validate(options, schema)
|
||||
|
||||
exports.defaults = {
|
||||
router: false,
|
||||
vuex: false,
|
||||
useTaobaoRegistry: null,
|
||||
packageManager: hasYarn ? 'yarn' : 'npm',
|
||||
plugins: {
|
||||
@@ -55,12 +59,12 @@ exports.loadOptions = () => {
|
||||
}
|
||||
}
|
||||
|
||||
exports.saveOptions = (toSave, deep) => {
|
||||
const options = exports.loadOptions()
|
||||
if (deep) {
|
||||
deepMerge(options, toSave)
|
||||
exports.saveOptions = (toSave, replace) => {
|
||||
let options
|
||||
if (replace) {
|
||||
options = toSave
|
||||
} else {
|
||||
Object.assign(options, toSave)
|
||||
options = Object.assign(exports.loadOptions(), toSave)
|
||||
}
|
||||
for (const key in options) {
|
||||
if (!(key in exports.defaults)) {
|
||||
@@ -78,15 +82,3 @@ exports.saveOptions = (toSave, deep) => {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const isObject = val => val && typeof val === 'object'
|
||||
|
||||
function deepMerge (to, from) {
|
||||
for (const key in from) {
|
||||
if (isObject(to[key]) && isObject(from[key])) {
|
||||
deepMerge(to[key], from[key])
|
||||
} else {
|
||||
to[key] = from[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,12 @@
|
||||
module.exports = cli => {
|
||||
cli.injectFeature({
|
||||
name: 'Router',
|
||||
value: 'router'
|
||||
})
|
||||
|
||||
cli.onPromptComplete((answers, options) => {
|
||||
if (answers.features.includes('router')) {
|
||||
options.router = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,2 +1,12 @@
|
||||
module.exports = cli => {
|
||||
cli.injectFeature({
|
||||
name: 'Vuex',
|
||||
value: 'vuex'
|
||||
})
|
||||
|
||||
cli.onPromptComplete((answers, options) => {
|
||||
if (answers.features.includes('vuex')) {
|
||||
options.vuex = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
const chalk = require('chalk')
|
||||
|
||||
module.exports = function formatFeatures (plugins, lead, joiner) {
|
||||
return Object.keys(plugins)
|
||||
.filter(dep => dep !== '@vue/cli-service')
|
||||
.map(dep => {
|
||||
dep = dep.replace(/^(@vue\/|vue-)cli-plugin-/, '')
|
||||
return `${lead || ''}${chalk.yellow(dep)}`
|
||||
})
|
||||
.join(joiner || ', ')
|
||||
module.exports = function formatFeatures (options, lead, joiner) {
|
||||
const features = []
|
||||
if (options.router) {
|
||||
features.push('vue-router')
|
||||
}
|
||||
if (options.vuex) {
|
||||
features.push('vuex')
|
||||
}
|
||||
const plugins = Object.keys(options.plugins).filter(dep => {
|
||||
return dep !== '@vue/cli-service'
|
||||
})
|
||||
features.push.apply(features, plugins)
|
||||
return features.map(dep => {
|
||||
dep = dep.replace(/^(@vue\/|vue-)cli-plugin-/, '')
|
||||
return `${lead || ''}${chalk.yellow(dep)}`
|
||||
}).join(joiner || ', ')
|
||||
}
|
||||
|
||||
@@ -9257,6 +9257,10 @@ vue-loader@^13.6.1:
|
||||
vue-style-loader "^3.0.0"
|
||||
vue-template-es2015-compiler "^1.6.0"
|
||||
|
||||
vue-router@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.1.tgz#d9b05ad9c7420ba0f626d6500d693e60092cc1e9"
|
||||
|
||||
vue-style-loader@^3.0.0, vue-style-loader@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-3.0.3.tgz#623658f81506aef9d121cdc113a4f5c9cac32df7"
|
||||
@@ -9285,6 +9289,10 @@ vue@^2.5.13:
|
||||
version "2.5.13"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.13.tgz#95bd31e20efcf7a7f39239c9aa6787ce8cf578e1"
|
||||
|
||||
vuex@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.0.1.tgz#e761352ebe0af537d4bb755a9b9dc4be3df7efd2"
|
||||
|
||||
walker@~1.0.5:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"
|
||||
|
||||
Reference in New Issue
Block a user