Vue.js2.0小白之旅-跟着大神去冒险

Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的 渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。Vue 的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合。另一方面,Vue 完全有能力驱动采用单文件组件和 Vue 生态系统支持的库开发的复杂单页应用。
Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。

项目实战

本次总结将项目中遇到坑及如何从零开始搭建Vue项目,Api官网简单教程
Vue-cli官网脚手架、vuex状态管理、vue-router路由管理

vue-cli

vue-cli 是vue.js的脚手架,用于自动生成vue.js模板工程的,安装vue-cli之前,需要先安装了vue和webpack。

1
2
3
4
5
6
7
安装及使用vue-cli
npm install -g vue
npm install -g webpack
npm install -g vue-cli
vue init webpack projectName
cd projectName
npm install //初始化安装依赖

此时项目安装完了,中间可以一直enter选取默认选项,生成的项目目录是这样的:

1.build和config都是webpack配置
2.src是存放项目源文件的目录
3.static 存放第三方静态资源,这个里面的gitkeep是这个目录为空,也可以把这个目录提交的git仓库里面,因为通常一个空目录是不能提交git仓库里面的
4.babelrc 是babel的配置项。
5.editorconfig是编辑器的配置项
6.gitignore里面存放的是会忽略语法检查的目录
7.index.html 入口页面
8.package.json是项目的描述和依赖
开始的时候执行npm run dev ,就相当于执行了这个文件里面的scripts的dev对应的 node build/dev-server.js。
然后执行npm run dev 在浏览器打开localhost:8080,默认配置端口8080,则可以看到欢迎页了
如何在我们自己的服务器上访问:
npm run build 会生成静态文件,在根目录的dist里,里面有个index.html,这是服务器访问的路径指定到这里就可以访问我们自己的项目了
注意:vue-cli生成的文件中使用的是ES6的语法,个人推荐阮一峰的Es6入门

monitor-web 插件及知识点文章

推荐webpack模板规范
推荐package.json属性查看
vue+webpack搭建的前端项目结构
vue基本模块用法
webpack babel属性
Vue-router路由
Vue-vuex状态管理
MDN import注解
vue-i18n中英文切换
vue生命周期每个阶段该做的事情
fetch详解

monitor-web Vue

monitor-web 目录结构

monitor-web build


1.index.html 入口文件
2.config.js 公共配置(port、babel配置、postcss设置)
3.server.js utils.js webpack.base.js webpack.dev.js webpack.prod.js webpack 基本配置

monitor-web client


1.components 单一的页面组件(包含Images文件夹)
2.css 抽离的css样式
3.filters 全局过滤函数,可使用管道符号进行控制
4.router 全局路由控制
5.store fetchApi.js url.js处理返回数据+权限控制 index.js vuex使用
6.views 拼装的单一界面
7.app.js 中英文切换、定义全局方法(filter)
8.index.js 挂载

monitor 其他项

.editorconfig 编辑器设置
.eslintrc Js语法检查
package.json 包管理

monitor-web 项目知识点

全局Filter+管道符号过滤

1
2
3
4
5
6
7
8
9
10
//定义方法
export function num(val){
return Math.round(val)
}
//全局过滤器注册
import * as myModule from "my-module";
导入整个模块的内容。以下代码将myModule添加到当前作用域,其中包括所有导出绑定。
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key])
})

中英文切换+或者多国语言切换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var Vue = require('vue')
var VueI18n = require('vue-i18n')
// ready translated locales
var locales = {
en: {
message: {
hello: 'hello world'
}
},
ja: {
message: {
hello: 'こんにちは、世界'
}
}
}
// install plugin
Vue.use(VueI18n)
// set lang
Vue.config.lang = 'en'
// set locales
Object.keys(locales).forEach(function (lang) {
Vue.locale(lang, locales[lang])
})

登录Login状态判定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
登录状态,外加此时的错误信息提示
logins() {
let username = this.user
let password = this.password
//username="admin"
//password="password"
if(username==''||password==''){
this.messagenull='用户名或密码不能为空'
}else{
this.messagenull=''
let user={username:username,password:password}
this.$store.dispatch('login',user)
if (this.login.id!= undefined) {
this.$store.dispatch('auth',this.login.auth)
store.set('msg', '1b1a6caa-c8bc-11e6-9dad-bc5436d20360')
store.set('lang', this.lang)
store.set('auth', this.login.auth)
router.push('/')
}
this.$watch(()=> {return this.login.id},(newVal, oldVal)=> {
if (this.login.id!= undefined) {
this.$store.dispatch('auth',this.login.auth)
store.set('msg', '1b1a6caa-c8bc-11e6-9dad-bc5436d20360')
store.set('lang', this.lang)
store.set('auth', this.login.auth)
router.push('/')
}
})
}
},

vue-router路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
mode: 'hash',
routes: [
{
path: '/login',
component:Login
},
{
path: '/',
component:MyHome,
children: [{
path: '',
component:StationMsg
},],
beforeEnter: requireAuth
},
{
path: '/stationDetail',
component:MyHome,
children: [{
path: '',
component:StationDetail
},],
beforeEnter: requireAuth
},
{
path: '/monitor',
component:MyHome,
children: [{
path: '',
component:MotionMonitor
},],
beforeEnter: requireAuth
},
{
path: '/analyse',
component:MyHome,
children: [{
path: '',
component:CountAnalyse
},],
beforeEnter: requireAuth
}
]
})

vue-vuex全局状态管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
const store = new Vuex.Store({
state,
mutations,
actions
})
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
count: 0,
indexOverview: {},
stationMsg:[],
motionMonitor:[],
countAnalyse:[],
waringInspect:[],
historyData:[],
}
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation
const mutations = {
INCREMENT(state) {
state.count++
},
TABLES(state, {
data
}) {
state.tableData = data
},
OVERVIEW(state, {data}) {
state.indexOverview = data
}
}
Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作。
const actions = {
incrementAsync({
commit
}) {
setTmimeout(() => {
commit('INCREMENT')
}, 200)
},
getTableData({
commit
}) {
fetch(api('data', {})).then(data => {
data.json().then(function(data) {
commit('TABLES', {
data
})
});
})
}
}

父子组件通信

prop

prop 是父组件用来传递数据的一个自定义属性。子组件需要显式地用 props 选项声明 “prop”:

1
2
3
4
5
6
7
8
Vue.component('child', {
// 声明 props
props: ['message'],
// 就像 data 一样,prop 可以用在模板内
// 同样也可以在 vm 实例中像 “this.message” 这样使用
template: '<span>{{ message }}</span>'
})
<child message="hello!"></child>

ref
vuex

import引入方式

1
2
3
4
5
6
7
8
9
import defaultMember from "module-name";
import * as name from "module-name";
import { member } from "module-name";
import { member as alias } from "module-name";
import { member1 , member2 } from "module-name";
import { member1 , member2 as alias2 , [...] } from "module-name";
import defaultMember, { member [ , [...] ] } from "module-name";
import defaultMember, * as name from "module-name";
import "module-name";

生命周期理解

生命周期图示,尤其结合下面的例子与图示更能理解生命周期的含义

html
1
2
3
4
5
6
<div class="test" style="border: 1px black dashed;padding: 8px;" @click="greet">
{{a}}
</div>
<div class="test2" style="border: 1px red solid;margin-top: 10px;padding: 8px;">
我是内容二
</div>
js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<script src="js/vue.js"></script>
<script type="text/javascript">
var myVue = new Vue({
el: ".test",
data: {
a: "我是内容,在控制台输入myVue.a=123456,可以改变我的值"
},
actived: function(){
console.log("该钩子在服务器端渲染期间不被调用")
},
deactivated: {
console.log("该钩子在服务器端渲染其间不被调用");
},
beforeCreate: function(){
console.log(this.a);
console.log('建立在属性之前');
},
created: function () {
console.log(this.a);
console.log("建立");
},
beforeMount: function () {
console.log("未开始编译");
},
beforeUpdate: function(){
console.log("数据更新前");
},
Updated: function(){
console.log("数据更新");
},
mounted: function () {
console.log("编译完成");
},
beforeDestroy: function () {
console.log("销毁前");
},
destroyed: function () {
console.log("已销毁");
},
methods:{
greet: function() {
console.log("123");
}
}
});
</script>

fetch + Es6 promise返回数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
fetch(url, init).then((res)=> {
let headers = res.headers;
if (res.ok) {
if(headers.has('x-auth-token')){
myauth=headers.get('x-auth-token')
//store.set('auth',headers.get('x-auth-token'))
}
if(headers.has('Content-Type')){
return res.json()
}else{
return {'ok':1}
}
} else if (res.status == 401) {
router.push('/login')
return res.json()
}else{
return res.json()
}
},(e)=> {
console.log("Error submitting form!")
console.log(e)
}).then((json)=>{
if(myauth!=''){
if(!_.isNumber(json))
json.auth=myauth
}
cb(json)
})
}

登录权限控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
if(method=="post"){
init={
headers: {
"Content-Type": "application/json",
"x-auth-token":auth
},
method: method,
body:JSON.stringify(body)
}
url=urlBase+path
}else{
init={
headers: {
"Content-Type": "application/json",
"x-auth-token":auth
},
}
var params = Object.keys(body)
.map((key) => encodeURIComponent(key) + "=" + encodeURIComponent(body[key]))
.join("&")
.replace(/%20/g, "+");
url=urlBase+path+'?'+params
}
//路由跳转之前进行的验证,beforeEnter
function requireAuth (to, from, next) {
let msg=store.get('msg')
let path=to.path
if (msg!='1b1a6caa-c8bc-11e6-9dad-bc5436d20360') {
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
next()
}
}

Table表格前端分页跟后端分页

前端分页 + 一次性获取所有的数据通过前端进行分页显示
1
2
3
4
5
6
7
8
9
tableData(){
let data = this.$store.state.RikiTableMonthPower
if (!_.isEmpty(data)) {
let total = data.length
let array = data
let arr = _.chunk(array, this.pageSize)
return arr[this.currentPage - 1]
}
},
后端分页 + 第一次只会获取当前第一页的数据之后会根据点击
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
import {router} from '../app'
export default {
data() {
return {
value1: '',
page:0,
pageSize: 5,
currentPage: 1,
loading: false,
par:''
}
},
methods: {
handleSizeChange(val) {
console.log(`每页 ${val} 条`);
this.pageSize = val
this.currentPage = 1
},
handleCurrentChange(val) {
let cname = this.$parent.$options.name
//this.loading = true
this.currentPage = val
//console.log(`当前页: ${val}`);
let params={}
let p=this.params
let no=p.no
let name=p.name
let company=p.company
let code=p.code
if(no!=undefined&&no!=''){
params.id=no
}
if(name!=undefined&&name!=''){
params.ownerName=name
}
if(company!=undefined&&company!=''){
params.distributorName=company
}
if(code!=undefined&&code!=''){
params.code=code
}
params.page=val
params.page=params.page-1
params.size=this.pageSize
params.type='day'
if(_.toString(this.datetime)!=''&&_.has(this.datetime,'type')){
params.type=_.toString(this.datetime.type)
}
if(_.toString(this.datetime)!=''&&_.has(this.datetime,'time')){
params.time=_.toString(this.datetime.time)
params.type=_.toString(this.datetime.type)
}
if(_.toString(this.datetime)!=''&&_.has(this.datetime,'startTime')){
params.startTime=_.toString(this.datetime.startTime)
params.endTime=_.toString(this.datetime.endTime)
}
// console.log(params);
this.$store.dispatch(cname,params)
},
uploadSuccess(response, file, fileList){
console.log(response);
console.log(file);
console.log(fileList);
},
handleRemove(file, fileList) {
console.log(file, fileList);
},
handlePreview(file) {
console.log(file);
},
add() {
router.push('addStation')
}
},
components: {
},
beforeMount() {
let name = this.$parent.$options.name
let pages = {}
pages.page = this.page
pages.size = this.pageSize
pages.type='day'
this.$store.dispatch(name, pages)
this.$store.dispatch('show')
this.$store.dispatch('clearparams')
},
watch: {
params: {
handler(val, oldVal) {
let cname = this.$parent.$options.name
let no=val.no
let name=val.name
let company=val.company
let code=val.code
let params={}
if(no!=undefined&&no!=''){
params.id=no
}
if(name!=undefined&&name!=''){
params.ownerName=name
}
if(company!=undefined&&company!=''){
params.distributorName=company
}
if(code!=undefined&&code!=''){
params.code=code
}
params.page=this.page
params.size=this.pageSize
params.type='day'
this.currentPage = 1
//if (pages.code.length != undefined) {
//this.loading = true
this.par=params
console.log(params);
this.$store.dispatch(cname,params)
//}
},
deep: true
},
datetime: {
handler(valtime, oldVal) {
let val=this.par
// console.log(val);
let cname = this.$parent.$options.name
let no=val.no
let name=val.name
let company=val.company
let code=val.code
let params={}
if(no!=undefined&&no!=''){
params.id=no
}
if(name!=undefined&&name!=''){
params.ownerName=name
}
if(company!=undefined&&company!=''){
params.distributorName=company
}
if(code!=undefined&&code!=''){
params.code=code
}
params.page=this.page
params.size=this.pageSize
this.currentPage = 1
//if (pages.code.length != undefined) {
//this.loading = true
params.type='day'
if(_.toString(valtime)!=''){
params.time=_.toString(valtime.time)
params.type=_.toString(valtime.type)
}
if(_.toString(valtime)!=''&&_.has(valtime,'time')){
params.time=_.toString(valtime.time)
params.type=_.toString(valtime.type)
}
if(_.toString(valtime)!=''&&_.has(valtime,'startTime')){
params.startTime=_.toString(valtime.startTime)
params.endTime=_.toString(valtime.endTime)
}
// console.log(params);
this.$store.dispatch(cname,params)
//}
},
deep: true
}
},
computed: {
datetime() {
return this.$store.state.datetime
},
params() {
return this.$store.state.params
},
ok() {
let path = this.$store.state.route.path
if (path.length == 1) {
return true
}else{
return false
}
},
total() {
let name = this.$parent.$options.name
return this.$store.state[name].totalElements
},
tableData() {
let name = this.$parent.$options.name
let data = this.$store.state[name].content
//this.loading = false
return data
}
}
};
文章目录
  1. 1. 项目实战
  2. 2. vue-cli
  3. 3. monitor-web 插件及知识点文章
  4. 4. monitor-web Vue
    1. 4.1. monitor-web 目录结构
    2. 4.2. monitor-web build
    3. 4.3. monitor-web client
    4. 4.4. monitor 其他项
  5. 5. monitor-web 项目知识点
    1. 5.1. 全局Filter+管道符号过滤
    2. 5.2. 中英文切换+或者多国语言切换
    3. 5.3. 登录Login状态判定
    4. 5.4. vue-router路由
    5. 5.5. vue-vuex全局状态管理
    6. 5.6. 父子组件通信
      1. 5.6.1. prop
      2. 5.6.2. ref
      3. 5.6.3. vuex
    7. 5.7. import引入方式
    8. 5.8. 生命周期理解
      1. 5.8.1. html
      2. 5.8.2. js
    9. 5.9. fetch + Es6 promise返回数据
    10. 5.10. 登录权限控制
    11. 5.11. Table表格前端分页跟后端分页
      1. 5.11.1. 前端分页 + 一次性获取所有的数据通过前端进行分页显示
      2. 5.11.2. 后端分页 + 第一次只会获取当前第一页的数据之后会根据点击
|