学习文章在这里。
1. 搭建环境
Nodejs与npm的安装不再叙述(希望大家装上的node版本大于等于6.x,不然还需要加上–harmony标志才可以开启es6),默认读者已经掌握npm安装依赖的方法。先安装npm,然后安装cnpm,这是淘宝源的npm。
我的评论,好像用cnpm install命令安装的文件好像不会自动修改package。
brew install npm
npm install -g cnpm --registry=https://registry.npm.taobao.org
首先全局安装
cnpm i vue-cli -g
vue init webpack demo
cd demo
cnpm install
cnpm run dev
到这里应该可以在http://localhost:8080/#/里看到欢迎界面
安装一些依赖,这里按照博文里的版本来
cnpm install koa@1.2.4 koa-router@5.4 koa-logger@1.3.0 koa-json@1.1.3 koa-bodyparser@2.3.0
再创建app.js文件,如下。
const app = require('koa')()
, koa = require('koa-router')()
, json = require('koa-json')
, logger = require('koa-logger'); // 引入各种依赖
app.use(require('koa-bodyparser')());
app.use(json());
app.use(logger());
app.use(function* (next){
let start = new Date;
yield next;
let ms = new Date - start;
console.log('%s %s - %s', this.method, this.url, ms); // 显示执行的时间
});
app.on('error', function(err, ctx){
console.log('server error', err);
});
app.listen(8889,() => {
console.log('Koa is listening in 8889');
});
module.exports = app;
新开一个terminal并输入
node app.js
Koa就打开完毕了,在8889端口
2. 前端页面构建
登陆界面
安装element-ui。
cnpm install element-ui@1.1.2
修改src/main.js
,这里有坑(主要表现为eslint那行注释不能删),按我的来
import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui' // 引入element-ui
import 'element-ui/lib/theme-default/index.css'
Vue.use(ElementUI) // Vue全局使用
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
在index.html中加入以下代码为了响应式页面
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
安装两个stylus依赖
cnpm install stylus@0.54.5 stylus-loader@2.4.0
创建src/components/Login.vue
<template>
<el-row class="content">
<el-col :xs="24" :sm="{span: 6,offset: 9}">
<span class="title">
欢迎登录
</span>
<el-row>
<el-input
v-model="account"
placeholder="账号"
type="text">
</el-input>
<el-input
v-model="password"
placeholder="密码"
type="password">
</el-input>
<el-button type="primary">登录</el-button>
</el-row>
</el-col>
</el-row>
</template>
<script>
export default {
data () {
return {
account: '',
password: ''
}
}
}
</script>
<style lang="stylus" scoped>
.el-row.content
padding 16px
.title
font-size 28px
.el-input
margin 12px 0
.el-button
width 100%
margin-top 12px
</style>
修改src/App.vue
<template>
<div id="app">
// 注意把下面的[]改成<>
[img src="./assets/logo.png"]
<Login></Login> <!--使用Login组件-->
</div>
</template>
<script>
import Login from './components/Login' // 引入Login组件
export default {
name: 'app',
components: {
Login // 注册组件
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
到这里应该可以看到http://localhost:8080/#/有了登陆欢迎界面
todolist 界面
还是在src/components目录下,写一个叫做TodoList.vue的文件。
<template>
<el-row class="content">
<el-col :xs="{span:20,offset:2}" :sm="{span:8,offset:8}">
<span>
欢迎:{{name}}!你的待办事项是:
</span>
<el-input placeholder="请输入待办事项" v-model="todos" @keyup.enter.native="addTodos"></el-input>
<el-tabs v-model="activeName">
<el-tab-pane label="待办事项" name="first">
<el-col :xs="24">
<template v-if="!Done"> <!--v-if和v-for不能同时在一个元素内使用,因为Vue总会先执行v-for-->
<template v-for="(item, index) in list">
<div class="todo-list" v-if="item.status == false">
<span class="item">
{{ index + 1 }}. {{ item.content }}
</span>
<span class="pull-right">
<el-button size="small" type="primary" @click="finished(index)">完成</el-button>
<el-button size="small" :plain="true" type="danger" @click="remove(index)">删除</el-button>
</span>
</div>
</template>
</template>
<div v-else-if="Done">
暂无待办事项
</div>
</el-col>
</el-tab-pane>
<el-tab-pane label="已完成事项" name="second">
<template v-if="count > 0">
<template v-for="(item, index) in list">
<div class="todo-list" v-if="item.status == true">
<span class="item finished">
{{ index + 1 }}. {{ item.content }}
</span>
<span class="pull-right">
<el-button size="small" type="primary" @click="restore(index)">还原</el-button>
</span>
</div>
</template>
</template>
<div v-else>
暂无已完成事项
</div>
</el-tab-pane>
</el-tabs>
</el-col>
</el-row>
</template>
<script>
export default {
data () {
return {
name: 'Molunerfinn',
todos: '',
activeName: 'first',
list:[],
count: 0
};
},
computed: { // 计算属性用于计算是否已经完成了所有任务
Done(){
let count = 0;
let length = this.list.length;
for(let i in this.list){
this.list[i].status == true ? count += 1 : '';
}
this.count = count;
if(count == length || length == 0){
return true
}else{
return false
}
}
},
methods: {
addTodos() {
if(this.todos == '')
return
let obj = {
status: false,
content: this.todos
}
this.list.push(obj);
this.todos = '';
},
finished(index) {
this.$set(this.list[index],'status',true) // 通过set的方法让数组的变动能够让Vue检测到
this.$message({
type: 'success',
message: '任务完成'
})
},
remove(index) {
this.list.splice(index,1);
this.$message({
type: 'info',
message: '任务删除'
})
},
restore(index) {
this.$set(this.list[index],'status',false)
this.$message({
type: 'info',
message: '任务还原'
})
}
}
};
</script>
<style lang="stylus" scoped>
.el-input
margin 20px auto
.todo-list
width 100%
margin-top 8px
padding-bottom 8px
border-bottom 1px solid #eee
overflow hidden
text-align left
.item
font-size 20px
&.finished
text-decoration line-through
color #ddd
.pull-right
float right
</style>
写完TodoList之后,我们需要将它和vue-router配合起来,从而使这个单页应用能够进行页面跳转。
页面路由
由于不采用服务端渲染,所以页面路由走的是前端路由。安装一下vue-router:cnpm install vue-router@2.1.1
.
这一部分需要改写main.js 和 App.vue,引入路由router。一个坑是eslint真的很严格,为了避免严格的错误,在/build/webpack-base.conf.js
中删除以下部分:
{
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter')
}
},
然后记得重新cnpm run dev
!! 这样就可以解除严格的eslint啦。
具体改后的代码如下,main.js挂载路由:
import Vue from 'vue'
import App from './App'
// import router from './router'
import ElementUI from 'element-ui' // 引入element-ui
import 'element-ui/lib/theme-default/index.css'
import VueRouter from 'vue-router'
Vue.use(ElementUI) // Vue全局使用
Vue.use(VueRouter)
import Login from './components/Login'
import TodoList from './components/TodoList'
const router = new VueRouter({
mode: 'history', // 开启HTML5的history模式,可以让地址栏的url长得跟正常页面跳转的url一样。(不过还需要后端配合,讲Koa的时候会说)
base: __dirname,
routes: [
{
path: '/', // 默认首页打开是登录页
component: Login // 注册login
},
{
path: '/todolist',
component: TodoList // 注册todolist
},
{
path: '*',
redirect: '/' // 输入其他不存在的地址自动跳回首页
}
]
})
/* eslint-disable no-new */
new Vue({
el: '#app',
router: router,
template: '<App/>',
components: { App }
})
App.vue把路由视图放到页面上:
<template>
<div id="app">
![](./assets/logo.png)
<router-view></router-view> <!-- 原本的Login换成了router-view 这就是路由视图渲染的目标元素-->
</div>
</template>
<script>
export default {
name: 'app',// 不需要再引入`Login`\`TodoList`组件了,因为在路由里已经注册了
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
这个时候你如果在地址栏后加上/todolist
那么就会跳转到TodoList页面啦。
下面改写一下Login.vue,就可以从login跳转到todolist了。
<!-- Login.vue -->
······
<!-- 给input增加键盘事件,当输入完密码回车也执行loginToDo方法 -->
<el-input
v-model="password"
placeholder="密码"
type="password"
@keyup.enter.native="loginToDo">
</el-input>
<!-- 增加一个click方法 loginToDo -->
<el-button type="primary" @click="loginToDo">登录</el-button>
······
<script>
export default {
data () {
return {
account: '',
password: ''
};
},
methods: {
loginToDo() {
this.$router.push('/todolist') // 编程式路由,通过push方法,改变路由。
}
}
};
</script>
实际上我们是单页应用,只是在应用内进行页面跳转而已,没有向后端额外请求。