本文基于工作项目开发,做的整理笔记
因工作需要,项目框架由最初的Java/jsp模式,逐渐转移成node/express模式。现在新项目又因为业务流程中的交互操作,会使页面dom根据数据data不断显示变化,便觉得此时是否是最佳实际,应该尝试一下vue框架。因为之前也偷偷的进行了一些vue框架的踩坑工作,已经填了一些坑或者有一些解决方案了,这也才敢用上。依然由后台Java提供API。本文就是做个笔记,说明如何使用Vue官方脚手架开始构建项目。
前提条件:
你已经听过、了解过Vue,至少一点点。
编码环境:
系统:OS X EI Capitan
版本:10.12.2
vue.jpg共2篇:
上篇:http://www.jianshu.com/p/ee2464501c65(当前)
下篇:http://www.jianshu.com/p/9baa03eb31fc
目录
| - 0.传送门
| - 1.安装
| - 2.项目初始化
| - 3.项目构建
| - Step1.阅读结构
| - Step2. 阅读代码(泛读)
| - 4.项目实操
| - Step1. 修改eslint配置
| - Step2. 调整结构
| - Step3. 添加登陆页面
| - Step4. 模版嵌套
| - (上下篇分割线)
| - Step5. 引入mockjs独立开发
| - Step6. 调用API接口
| - Step7. 状态管理(store仓库)
| - Step8. 抽离公共方法
| - Step9. 剥离组件
| - Finish. 生成打包
| - 5.项目部署
| - a)本地部署
| - b)服务器部署
| - 6.结束
0.传送门
如果你是直接<script>引用vue.js,去写一些实例或进行页面开发的话,那么你可能去看一官网的一些知识点,以下内容可能不适合你。
官网‘起步’传送门:http://cn.vuejs.org/v2/guide/#起步
1.安装
你已经安装了npm,它是随node.js安装的,装了node.js也就有了它。
node.js安装下载地址:http://nodejs.cn/download/
# 检查node.js是否安装,若有则显示版本号
$ node -v
# 检查npm的版本号
$ npm -v
# 若要更新npm,使用
$ npm install npm@latest -g
一般,我们还会安装淘宝镜像cnpm,因为在墙内,有时候使用npm安装会很慢,所以需要。
# 安装cnpm,并指定镜像地址
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
个人一点点喜好(不介意的话无所谓):
如果能用npm,我就不太喜欢用cnpm。原因你自己通过npm install / cnpm install安装后,去看看node_modules文件夹内的文件就可以了。
写文章时,我的目前版本如下:
Yuxinde-MacBook-Pro:~ yuxin$ node -v
v6.2.0
Yuxinde-MacBook-Pro:~ yuxin$ npm -v
3.8.9
Yuxinde-MacBook-Pro:~ yuxin$ cnpm -v
cnpm@4.5.0 (/usr/local/lib/node_modules/cnpm/parse_argv.js)
npm@3.10.10 (/usr/local/lib/node_modules/cnpm/node_modules/npm/lib/npm.js)
node@6.2.0 (/usr/local/bin/node)
npminstall@2.29.1 (/usr/local/lib/node_modules/cnpm/node_modules/npminstall/lib/index.js)
prefix=/usr/local
darwin x64 16.3.0
registry=https://registry.npm.taobao.org
vue的安装:
# 最新稳定版
$ npm install vue
# 同时安装它的命令行工具
$ npm install --global vue-cli
安装好之后我们接下来就使用它的命令行工具vue-cli构建项目。类似的还有在react.js方面,快速上手会使用到‘蚂蚁金服’的Ant-design,使用antd-init或者dva-cli,都是命令行构建项目。
2.项目初始化
使用命令行工具,开始一点点构建项目:
# 去到放项目的文件夹地址
Yuxinde-MacBook-Pro:~ yuxin$ cd /Users/yuxin/Documents/Season/Project/Vue
# 创建基于webpack模版的项目,名称为vue-demo
Yuxinde-MacBook-Pro:Vue yuxin$ vue init webpack vue-demo
# 提示我有新版本
A newer version of vue-cli is available.
latest: 2.8.2
installed: 2.8.1
This will install Vue 2.x version of the template.
# 提示我如果需要使用webpack#1.0的话,创建时用这个命令
For Vue 1.x use: vue init webpack#1.0 vue-demo
# 填写了一些基础信息,根据你的需要选择
# 这里我用了ESLint,标准选择了默认的Standard,不熟悉ESLint的话建议不启用或标准选择none,我是给自己挖坑的,玩玩,我不熟。
# 这里我不启动unit测试和e2e测试
? Project name vue-demo
? Project description This is a demo by season
? Author Season <yuxin0721@gmail.com>
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? Yes
? Pick an ESLint preset Standard
? Setup unit tests with Karma + Mocha? No
? Setup e2e tests with Nightwatch? No
vue-cli · Generated "vue-demo".
To get started:
cd vue-demo
npm install
npm run dev
Documentation can be found at https://vuejs-templates.github.io/webpack
先尝试跑起来,看看项目初始化最初的效果。
Yuxinde-MacBook-Pro:Vue yuxin$ cd vue-demo
Yuxinde-MacBook-Pro:vue-demo yuxin$ npm install
fetchMetadata → network ▀ ╢███████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░╟
^C
# 安装很慢,所以我control+r停止了安装,尝试使用cnpm install去安装
Yuxinde-MacBook-Pro:vue-demo yuxin$ cnpm install
# 此处省略安装信息,安装瞬间完成
Yuxinde-MacBook-Pro:vue-demo yuxin$ npm run dev
> vue-demo@1.0.0 dev /Users/yuxin/Documents/Season/Project/Vue/vue-demo
> node build/dev-server.js
> Starting dev server...
DONE Compiled successfully in 3800ms 09:06:56
> Listening at http://localhost:8080
浏览器查看,效果如下:
项目初始化效果.png3.项目构建
接下来,我们就一步步来完善这个项目。但是我想先讨论一个问题,题外话。
插入一小段题外话:你是如何学习、快速上手,并逐步使用一个框架的?
我个人的一个小小想法是这个样子的:
无论是听别人的经验或是自己实践后,都会得到这样一个命题“如果一个东西自己都不会使用,就开始学的话,学会也蛮难的”。其实,也就是说如果不去先用、先去体验,拥有自己的感受、自己的想法、以至于有自己的思考,这样才会得到一个认识,而不是盲目的跟着教程一步一步,一定要思考。
比如,我想学vue.js,我一定是有一个目的,我想用它做什么,其次才是我怎样去学、去用。我的快速上手,是这样的。
- 搞清楚我要做什么,我的目的,我的需求;
- 找到官网,了解知识,这里就是了解vue.js;
- 找demo,比如说,去玩它的功能,和我的需求去匹配,不满足就找下一个demo,直到对比多个之后“嗯,这个差不多,就先玩下这个”;
- 几个demo下来,其实你会觉得可能其中某个demo也有你欣赏的功能、欣赏的地方,那留着日后你可能参考、整合、或重写;
- 确定了demo框架,就开始看它的结构,其实也会大概的看其他demo的结构,有好的地方会搬过来优化这个框架结构,经常是相互借鉴的;
- 阅读demo的代码,根据我的需求,去编写实现我的功能的代码。遇到问题就爬坑学习,解决问题的经验不断积累,或者又看了其他人的demo,从而不断优化整个架构。
- 最后就是,玩的差不多的时候。可以从项目初始化创建开始,自己动手搭建一个,加深学习啦。
Step1. 阅读结构
初始化的项目结构如下所示:(其中加粗为文件夹,斜体为文件)
vue-demo
| - build (运行文件夹,dev/prod运行时需要用到的相关文件,含webpack配置)
| - config (配置文件夹,比如说dev/prod两种对应的不同接口等设置)
| - dist (打包生成的部署文件夹,一开始没有的,跑命令npm run build之后可见!!!)
| - node_modules
| - src (项目代码)
| - assets (资源文件夹,为了区分static,这里存自己的资源)
| - components (页面文件夹,题外:其实我觉得这个像组件,页面应该是views,先保留这样)
| - router (路由文件夹)
| - App.vue (vue主文件)
| - main.js (入口文件,webpack配置的entrance)
| - static (资源文件夹,为了区分assets,这里存第三方的资源)
| - .babelrc (转码规则配置)
| - .editorconfig (代码规范配置)
| - .eslintignore (eslint检查忽略配置)
| - .eslintrc.js (eslint配置文件)
| - .gitignore (git忽略配置)
| - .postcssrc.js (postcss的load的config,文件里有对应的github地址)
| - index.html (html主页面)
| - package.json
| - README.md
Step2. 阅读代码(泛读)
各部分结构已经基本掌握了,部分内容去谷歌了解一下,直接看src
文件夹的代码。
1)index.html
先看一下index.html
里的代码是什么吧。这里唯一需要留意的是下面这两行,告诉我们有一个#app的DOM元素,还有就是会自动插入生成后的JS脚本。
<div id="app"></div>
<!-- built files will be auto injected -->
2)main.js
在main.js
这个文件里,就做了一件事,声明了一个Vue根实例,主要是说明用APP组件去渲染替换#app的这个DOM元素。
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App' // 引入App.vue文件
import router from './router' // 引入router文件夹里的index.js(文件夹就是默认找index.js文件,没有报错)
Vue.config.productionTip = false // 设置为 false 以阻止 vue 在启动时生成生产提示,可以查API文档
/* eslint-disable no-new */
new Vue({
el: '#app', // id为app的DOM元素,将被挂载(替换)的元素
router, // 引入路由,可以查看router文档
template: '<App/>', // 将组件包装成这样一个模版,并且将它挂载(替换)到#app这个DOM上
components: { App } // 引入组件,这个组件相当于一个大容器,用于承载我们其他的页面
})
让我们回头看一下DEMO页面,上面明确的提示我们,可以参考的一些有用的网站:
vue的API文档:http://cn.vuejs.org/v2/api/#全局配置
vue-router路由:http://router.vuejs.org/zh-cn/
vue-loader加载器:http://vue-loader.vuejs.org/zh-cn/
vuex状态管理:http://vuex.vuejs.org/
3)App.vue
查看App.vue
这个文件,里面主要由3部分组成【template】、【script】、【style】,分别对应的就是页面里的html、js、css。这里唯一留意的就是<router-view></router-view>
了,因为在demo页面中,我们看到了一张图片之外,还有下面的文字链接内容,这就要去了解路由了。
<template>
<div id="app">
![](./assets/logo.png)
<router-view></router-view> <!-- 路由的页面将会被嵌入到这里 -->
</div>
</template>
<script>
export default {
name: 'app'
}
</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>
4)router/index.js
看一下路由index.js
文件,它引入了vue-router路由管理,还引入了一个Hello组件。
我们需要做的就是将组件(components)映射到路由(routes),然后告诉 vue-router 在哪里渲染它们。routes
数组里就是放着N对映射关系。
import Vue from 'vue'
import Router from 'vue-router'
import Hello from '@/components/Hello' // 引入Hello组件
Vue.use(Router)
export default new Router({
routes: [
/****** 一对映射 start *******/
{
path: '/',
name: 'Hello',
component: Hello
}
/****** 一对映射 end *******/
// 下一对,如果有的话,别忘了逗号
// ...
]
})
5)components/Hello.vue
最后,看一下Hello.vue
文件,【template】代码和平时我们写的html基本是一样的,除了用到一些变量、语法,比如这样:
-
{{ msg }}
// msg是变量 -
:class="myclass"
// myclass是变量 -
:ref="'mass.'+index+'.detail'
// index是变量 -
v-if="flag"
// flag是变量 -
v-show="page=='Hello'"
// page是变量 -
v-for="(item, index) in list"
// list是变量 -
v-html="myHtml"
// myHtml是变量 - ...
<!-- Hello.vue的template部分 -->
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<h2>Essential Links</h2>
<ul>
<li><a href="https://vuejs.org" target="_blank">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank">Forum</a></li>
<li><a href="https://gitter.im/vuejs/vue" target="_blank">Gitter Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank">Twitter</a></li>
<br>
<li><a href="http://vuejs-templates.github.io/webpack/" target="_blank">Docs for This Template</a></li>
</ul>
<h2>Ecosystem</h2>
<ul>
<li><a href="http://router.vuejs.org/" target="_blank">vue-router</a></li>
<li><a href="http://vuex.vuejs.org/" target="_blank">vuex</a></li>
<li><a href="http://vue-loader.vuejs.org/" target="_blank">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank">awesome-vue</a></li>
</ul>
</div>
</template>
那接着看一下【script】,这个项目初始化的例子代码比较简单,只有一个data(name忽视,没什么特别的)。这里其实需要了解很多方法,他们都是用来干什么的,怎么用,什么时候用。我们稍后来看一下。
<!-- Hello.vue的script部分 -->
<script>
export default {
name: 'hello',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
}
}
</script>
那接着看一下【style】,和平时写的没什么区别。这里重点留意下这个关键字scoped
,代码这里有一个注释,说“添加这一关键字表示这些样式被限制在这个组件内,不会污染其他组件”。
<!-- Hello.vue的style部分 -->
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
大多时候,基本都会添加
scoped
关键字,不过比如我们在使用elementUI(或者其他UI)的时候,我们需要覆盖它的样式,那可能需要添加多一个<style></style>
,注意这个不带scoped
关键字。一般把覆盖的样式独立成一个单独的css文件,通过每个组件的一个个性化class去区分设置多个同样ui的不同样式。
<!-- element-UI.css UI样式覆盖文件 -->
<style>
...
</style>
6)常用方法
先看一下官网的这张“生命周期图示”,正如官网所说,一时看不懂,但是用一用就看懂了,记得回来再看看。
lifecycle.png如果你看得懂这张图,你就会发现App.vue
刚好是黄色棱形判断的一种,然后其他components的xxx.vue(比如这里只有一个Hello.vue)
就是判断的另外一种。先不多说这张图,在玩的过程中实现你的demo就清楚了。
我们常用的方法有如下一些:
- props,页面不会有,但是组件基本都会有,主要用于传数据
// 举例
props:{
id: {
type: String,
default: ''
},
status: {
type: Number,
default: 0
}
},
- watch,监视页面某一个值的变化,由它变化可以导致其他变化
// 举例
watch:{
status(val) {
if(val == 1) {
this.value = 'aaaa';
} else {
this.value = 'bbbb';
}
}
},
- components,引入组件的时候,我们要在这里声明
// 举例
components: {
AddDialog, // 组件1
ContainerFooter // 组件2
},
- beforeRouteEnter,在页面进入前要执行的一些操作放在这里,比如请求数据
// 举例
beforeRouteEnter (to, from, next) {
getActData(params).then(response => {
// 拿回结果后才next()
...
next(vm => {
// 赋值
....
});
}).catch(err => {
....
});
},
- created,在页面创建后执行的操作,有些请求也可以放这里
// 举例
created() {
// 加载列表数据
this.status = this.$route.query.status == undefined ? 1 : this.$route.query.status;
this.fetchData();
},
- computed,比如我根据某个变量做的不同显示、判断,都可以变成一个方法放这里,以便调用
// 举例
computed: {
confirmText() {
return this.edit?'立即修改':'立即创建';
},
},
- filters,过滤器,用来将一些值格式化输出显示,比如
枚举
// 举例
filters: {
toDateShort(val) {
return val.substring(0,11);
}
},
- methods,整个页面调用的方法,
click
事件、change
事件都在这里
// 举例
methods: {
showLoading() {
this.loading = true
},
hideLoading() {
this.loading = false
},
},
浏览vue.js的api文档https://cn.vuejs.org/v2/api/,还是有好多东西在不断的开发过程中会被用到。比如slot
也是一个很值得研究的东西。
4.项目实操
接下来,我们就一起实现一个小demo,但它的结构应该是比较完善的,至少可以较好的满足大部分场景和功能需求。
Step1. 修改eslint配置
试了一下,standard标准检查出现的错误提示实在太多了,换一下自定义标准,这个没那么严格,不会那么尴尬。
# 新的配置,用来替换原来的标准
module.exports = {
root: true,
parser: 'babel-eslint',
"evn": {
"node": true,
"commonjs": true,
"shared-node-browser": true,
"browser": true
},
parserOptions: {
sourceType: 'module'
},
env: {
browser: true,
"es6": true,
"node": true
},
// https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
extends: 'eslint:recommended',
// required to lint *.vue files
plugins: [
'html'
],
// add your custom rules here
'rules': {
// allow paren-less arrow functions
'arrow-parens': 0,
// allow async-await
'generator-star-spacing': 0,
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
// allow console
'no-console': ["error", { allow: ["warn", "error"] }]
}
}
Step2. 调整结构
先调整一下结构吧,因为后面会陆续用到。考虑到后面一个个添加,会没有一个整体直观的印象,感觉有点乱吧,我个人不太喜欢,就先整体了解一下。
注意:有注释的说明是刚刚新添加的。
vue-demo
| - build
| - config
| - dist
| - node_modules
| - src
| - api (api接口文件夹)
| - assets
| - components (组件文件夹,原来保留)
| - Hello.vue (转移到views目录下------>)
| - mock (mock.js假数据接口文件夹)
| - router
| - store (仓库文件夹)
| - styles (样式文件夹)
| - utils (公共方法文件夹)
| - vendor (第三方库文件夹)
| - views (页面文件夹)
| - dashboard (看板页面)
| - tablelist (列表页面)
| - error (错误页面)
| - layout (模版)
| - login (登陆页面)
| - hello (原demo的Hello页面)
| - Hello.vue (------>转移后的位置)
| - App.vue
| - main.js
| - static
| - .babelrc
| - .editorconfig
| - .eslintignore
| - .eslintrc.js
| - .gitignore
| - .postcssrc.js
| - index.html
| - package.json
| - README.md
让我们使用npm run dev
运行一下demo,会发现报错“在./src/router/index.js
里找不到引用的Hello.vue
文件”,如下:
Yuxinde-MacBook-Pro:vue-demo yuxin$ npm run dev
> vue-demo@1.0.0 dev /Users/yuxin/Documents/Season/Project/Vue/vue-demo
> node build/dev-server.js
> Starting dev server...
ERROR Failed to compile with 1 errors 09:49:17
This dependency was not found:
* @/components/Hello in ./src/router/index.js
To install it, you can run: npm install --save @/components/Hello
> Listening at http://localhost:8080
这里你需要改一下引用路径,因为刚刚我们调整过结构。修改之后,发现页面正常了,OK。后面我们不会用到Hello.vue
这一块,但暂时保留吧。
# ./src/router/index.js
# 将这句
import Hello from '@/components/Hello'
# 改为
import Hello from '@/views/hello/Hello'
Step3. 添加登陆页面
从这里开始,后面的每一步可能会涉及到使用npm install xxx
去安装一些模块,来服务项目的创建、运行。
安装element-ui,官方网站:http://element.eleme.io/#/zh-CN/component/installation。
Yuxinde-MacBook-Pro:vue-demo yuxin$ npm install element-ui --save
npm WARN skippingAction Module is inside a symlinked module: not running update node-pre-gyp@0.6.36 node_modules/fsevents/node_modules/node-pre-gyp
npm WARN skippingAction Module is inside a symlinked module: not running move co@4.6.0 node_modules/ajv-keywords/node_modules/ajv/node_modules/co
npm WARN skippingAction Module is inside a symlinked module: not running move json-stable-stringify@1.0.1 node_modules/ajv-keywords/node_modules/ajv/node_modules/json-stable-stringify
npm WARN skippingAction Module is inside a symlinked module: not running add ajv@4.11.8 node_modules/ajv-keywords/node_modules/ajv
vue-demo@1.0.0 /Users/yuxin/Documents/Season/Project/Vue/vue-demo
└─┬ element-ui@1.3.7
├── async-validator@1.6.9
├── babel-helper-vue-jsx-merge-props@2.0.2
├── deepmerge@1.4.4
└── throttle-debounce@1.0.1
# 然后把项目跑起来
Yuxinde-MacBook-Pro:vue-demo yuxin$ npm run dev
在./src/views/login
下添加login.vue(也可以index.vue,但是为了清晰,先不这么写)
文件,等下编写login.vue
的代码。先修改一下路由配置./src/router/index.js
,因为等下要访问:
vue-demo
| -
- |
---|
| - src
| -
| - views
| -
| - login
| - login.vue (新增)
- |
---|
# ./src/router/index.js
import Login from '@/views/login/login' // 新增
...
routes: [
{
path: '/',
name: 'Hello',
component: Hello
}, // 注意逗号
// 新增
{
path: '/login',
name: 'Login',
component: Login
}
]
因为我们已经跑起来了项目,在浏览器里访问login看看,http://localhost:8080/#/login 。我们看到一个图片,还发现有个错误,大概说template找不到,先不理它,因为我们的login.vue
还是空白的。
我们注意到这样一个问题,为何路由是
/#/login
这个样子的,这里你的项目可以继续保持这样,但不喜欢也可以改变它。这里我们改变路由router
的模式,由默认的hash
变为history
。
相关阅读:https://router.vuejs.org/zh-cn/api/options.html#mode。
# ./src/router/index.js
export default new Router({
mode: 'history', // 新增
routes: [
...
之后的访问路径就变为http://localhost:8080/login。刚刚我们发现登陆页面出现了一个大大的logo图片,现在把它去掉,修改./src/App.vue
文件,如下:
# ./src/App.vue
<template>
<div id="app">
![](./assets/logo.png) // 去掉这一句,删除
...
我们将会用到element-ui,去./src/main.js
里把它引入进来,如下:
# ./src/main.js
...
import ElementUI from 'element-ui'; // 新增
import 'element-ui/lib/theme-default/index.css'; // 新增
...
Vue.use(ElementUI); // 新增
/* eslint-disable no-new */
new Vue({
...
编辑./src/views/login/login.vue
,添加代码如下:
# ./src/views/login/login.vue
<template>
<div class="login-container">
<el-form autoComplete="on" :model="loginForm" :rules="loginRules" ref="loginForm" label-position="left" label-width="0px" class="card-box login-form">
<h3 class="title">系统登录</h3>
<el-form-item prop="email">
<span class="svg-container"><i class="el-icon-menu"></i></span>
<el-input name="email" type="text" v-model="loginForm.email" autoComplete="on" placeholder="邮箱"></el-input>
</el-form-item>
<el-form-item prop="password">
<span class="svg-container"><i class="el-icon-setting"></i></span>
<el-input name="password" type="password" @keyup.enter.native="handleLogin" v-model="loginForm.password" autoComplete="on" placeholder="密码"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" style="width:100%;" :loading="loading" @click.native.prevent="handleLogin">
登录
</el-button>
</el-form-item>
<div class='tips'>直接点登陆就可以登录成功...</div>
<div class='tips'>如果想试试验证,建议删除输入框字符试试...</div>
</el-form>
</div>
</template>
<script>
export default {
data() {
// 验证邮箱的规则
const validateEmail = (rule, value, callback) => {
if (!this.isEmail(value)) {
callback(new Error('请输入正确的合法邮箱'));
} else {
callback();
}
};
// 验证密码的规则
const validatePass = (rule, value, callback) => {
if (value.length < 6) {
callback(new Error('密码不能小于6位'));
} else {
callback();
}
};
return {
loginForm: {
email: 'admin@fusio.com.cn',
password: '123456'
},
loginRules: {
email: [
{ required: true, trigger: 'blur', validator: validateEmail }
],
password: [
{ required: true, trigger: 'blur', validator: validatePass }
]
},
loading: false,
showDialog: false
}
},
methods: {
isEmail(str) {
const reg = /^[a-z0-9](?:[-_.+]?[a-z0-9]+)*@fusio\.com\.cn$/i;
return reg.test(str.trim());
},
handleLogin() {
// 验证表单
this.$refs.loginForm.validate(valid => {
if (valid) {
// 验证通过
this.loading = true;
if(this.loginForm.email == 'admin@fusio.com.cn' && this.loginForm.password == '123456') {
// 注意这里有2种方法
// 注意下不同写法时this的指向问题,用=>可以保持this的固定指向
// 方法1
// let that = this;
// setTimeout(function(){
// that.loading = false;
// that.$router.push({ path: '/' });
// }, 1000);
// 方法2
setTimeout(() => {
this.loading = false;
this.$router.push({ path: '/' });
}, 1000)
}
} else {
// 验证失败
console.error('error submit!!');
return false;
}
});
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoprd>
.tips{
text-align: left;
font-size: 14px;
color: #999;
margin-bottom: 5px;
}
.login-container {
position: relative;
width: 100%;
height: 100%;
height: 100vh;
background-color: #2d3a4b;
input:-webkit-autofill {
-webkit-box-shadow: 0 0 0px 1000px #293444 inset !important;
-webkit-text-fill-color: #fff !important;
}
input {
background: transparent;
border: 0px;
-webkit-appearance: none;
border-radius: 0px;
padding: 12px 5px 12px 15px;
color: #eeeeee;
height: 47px;
}
.el-input {
display: inline-block;
height: 47px;
width: 85%;
}
.svg-container {
padding: 6px 5px 6px 15px;
color: #889aa4;
}
.title {
font-size: 26px;
font-weight: 400;
color: #eeeeee;
margin: 0px auto 40px auto;
text-align: center;
font-weight: bold;
}
.login-form {
position: absolute;
left: 0;
right: 0;
width: 400px;
padding: 35px 35px 15px 35px;
margin: 120px auto;
}
.el-form-item {
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.1);
border-radius: 5px;
color: #454545;
}
.forget-pwd {
color: #fff;
}
}
</style>
发现需要调整全局样式,比如body
。我们把样式抽离出./src/App.vue
,集中到单独的./src/styles/index.scss
,注意这里开始用scss
,反正都抽出来了。最后在./src/main.js
文件中引用。
vue-demo
| -
| - src
| -
| - styles
| - index.scss (新增)
| -
| -
题外话:如果您只是抽成index.css,那就不用执行npm install
去安装scss
需要的模块。
# 安装sass需要的模块
Yuxinde-MacBook-Pro:vue-demo yuxin$ npm install sass-loader --save
npm WARN skippingAction Module is inside a symlinked module: not running update node-pre-gyp@0.6.36 node_modules/fsevents/node_modules/node-pre-gyp
npm WARN skippingAction Module is inside a symlinked module: not running move co@4.6.0 node_modules/ajv-keywords/node_modules/ajv/node_modules/co
npm WARN skippingAction Module is inside a symlinked module: not running move json-stable-stringify@1.0.1 node_modules/ajv-keywords/node_modules/ajv/node_modules/json-stable-stringify
npm WARN skippingAction Module is inside a symlinked module: not running add ajv@4.11.8 node_modules/ajv-keywords/node_modules/ajv
vue-demo@1.0.0 /Users/yuxin/Documents/Season/Project/Vue/vue-demo
├── UNMET PEER DEPENDENCY node-sass@^4.0.0
└─┬ sass-loader@6.0.6
├─┬ clone-deep@0.3.0
│ ├── for-own@1.0.0
│ ├─┬ is-plain-object@2.0.3
│ │ └── isobject@3.0.0
│ ├── kind-of@3.2.2
│ └─┬ shallow-clone@0.1.2
│ ├── kind-of@2.0.1
│ ├── lazy-cache@0.2.7
│ └─┬ mixin-object@2.0.1
│ └── for-in@0.1.8
├── lodash.tail@4.1.1
└── pify@3.0.0
npm WARN sass-loader@6.0.6 requires a peer of node-sass@^4.0.0 but none was installed.
# 这里它提示我说node-sass没有安装
Yuxinde-MacBook-Pro:vue-demo yuxin$ npm install node-sass --save
(安装过程...略)
# 重新运行项目
Yuxinde-MacBook-Pro:vue-demo yuxin$ npm run dev
上面我们只是安装了scss
用到的模块,然后去查看一下./build/webpack.base.conf.js
里的配置代码,你发现它把各种css-loader
整合在一个方法放在了./build/utils.js
文件里,自己不需要再添加任何配置。
其他几个文件的代码修改如下:
# ./src/App.vue
...
// 删除style整块内容
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
</style>
...
# ./src/styles/index.css
// 全部为添加
body {
margin: 0;
}
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
# ./src/main.js
...
import './styles/index.scss'; // 新增
...
现在我们访问http://localhost:8080/login,修改输入可以看到表单检查,直接点登录,则登录成功在1s后跳转首页。这里的1s只是为了看一下loading效果。具体代码里的一些vue基础知识,可以查阅官网。
Step4. 模版嵌套
前面我们已经能够实现单个无模版页面的编写,现在去看一下模板嵌套吧,这个在项目搭建中还是必须要使用的呢。
那我们先在./src/views/layout
文件夹下创建这几个文件,分别是AppMain.vue
、 index.js
、 Layout.vue
、 Navbar.vue
和Sidebar.vue
。然后在./src/views/dashboard
文件夹下创建dashboard.vue
,它将使用Layout.vue
模版,这样我们才能看到效果。对应的新增文件结构如下:
vue-demo
| -
| - src
| -
| - views
| -
| - dashboard
| - * dashboard.vue* (看板页面,新增)
| - layout
| - AppMain.vue (页面容器,新增)
| - index.js (新增)
| - Layout.vue (模版框架,新增)
| - Navbar.vue (顶部导航栏,新增)
| - Sidebar.vue (侧边菜单栏,新增)
| - 更多还有面包屑层级路径(略)
| -
| -
我们先不去给上面新建的各个文件添加代码,我们先去修改一下./src/router/index.js
文件,创建dashboard
页面的路径映射关系,调整一下hello
页面的路径映射关系。修改代码如下:
# ./src/router/index.js 当前的完整代码
import Vue from 'vue'
import Router from 'vue-router'
import Hello from '@/views/hello/Hello'
import Login from '@/views/login/login'
import Dashboard from '@/views/dashboard/dashboard'
// Layout引入(这里采用这种写法2,不再提示)
// import Layout from '../views/layout/Layout'; // 写法1,通过../层级查找
import Layout from '@/views/layout/Layout'; // 写法2,@默认指向src文件夹,在./build/webpack.base.conf.js的alias可以配置
Vue.use(Router)
export default new Router({
mode: 'history',
// 注意
// routes这里可以好多种结构写法,自己回头研究下吧
routes: [
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [
{ path: '/dashboard', component: Dashboard, name: 'Dashboard' },
{ path: '/hello', component: Hello, name: 'Hello' }
]
},
]
})
这时候,我们去访问http://localhost:8080/dashboard,它提示了我一个之前遇到过的template or render function not defined.
错误,由于空白文件引起。现在我们去编辑一下./src/views/dashboard/dashboard.vue
和```./src/views/layout/Layout.vue``文件,因为它们现在都是空白的。
# ./src/views/dashboard/dashboard.vue
<template>
<p>This is dashboard page.</p>
</template>
<script>
export default {
}
</script>
# ./src/views/layout/Layout.vue 临时代码
# 模版框架
<template>
<div class="app-wrapper">
<p>This is a demo for learning vue.</p>
<hr/>
<router-view></router-view>
</div>
</template>
<script>
export default {
}
</script>
再次访问http://localhost:8080/dashboard,发现页面正常无报错,并且显示了两个页面的两句话,说明模板框架已经被成功引用。下面我们快速的应用框架代码,借用比较常用的后台框架,学习模版嵌套使用。它们各文件的代码如下所示:
# ./src/views/layout/Layout.vue 新代码!!!
# 模版框架
<template>
<div class="app-wrapper">
<div class="navigator-wrapper">
<div class="navigator-container">
<div class="logo"></div>
<Sidebar />
</div>
</div>
<div class="main-container">
<Navbar />
<App-main />
</div>
</div>
</template>
<script>
// 引入组件文件
import { Navbar, Sidebar, AppMain } from '@/views/layout'; //也就是指向./layout/index.js
export default {
// 名称(可不写)
name: 'layout',
// 使用组件
components: {
Navbar,
Sidebar,
AppMain
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.app-wrapper {
padding-left: 190px;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
&:after {
content: "";
display: table;
clear: both;
}
.navigator-wrapper {
display: block;
position: absolute;
z-index: 1000;
width: 190px;
left: 0;
top: 0;
bottom: 0;
overflow: hidden;
background: #2F323A;
transition: all .28s ease-out;
&::-webkit-scrollbar-track-piece {
background: #d3dce6;
}
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
background: #99a9bf;
border-radius: 20px;
}
}
.navigator-wrapper a:focus,
.navigator-wrapper a:active {
outline: none;
}
.navigator-container {
transition: all .28s ease-out;
}
.logo {
box-shadow: -1px 0 0 1px #000;
width: 190px;
height: 70px;
display: block;
background-color: #111111;
background-image: url(../../assets/logo.png);
background-repeat: no-repeat;
background-size: contain;
background-position: 50%;
overflow: hidden;
}
.main-container {
width: 100%;
height: 100%;
overflow-y: hidden;
transition: all .28s ease-out;
}
}
</style>
# ./src/views/layout/AppMain.vue
# 页面容器
<template>
<section class="app-main" ref="wrap">
<transition name="fade" mode="out-in">
<router-view :key="key">
</router-view>
</transition>
</section>
</template>
<script>
export default {
name: 'AppMain',
computed: {
key() {
return this.$route.name !== undefined
? this.$route.name + +new Date()
: this.$route + +new Date()
}
},
methods:{
scrollBottom() {
var foot = this.$refs['wrap'].getElementsByClassName('container-footer')[0];
foot?foot.style.bottom = -this.$refs.wrap.scrollTop + 'px':null;
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.app-main {
position: relative;
height: 100%;
overflow-y: auto;
background-color: #f5f5f5;
}
</style>
# ./src/views/layout/Navbar.vue
# 顶部导航栏
<template>
<div class="navbar">
<div class="info-container">
<a href="javascript:;" @click="logout"><i class="el-icon-upload2"></i> 退出</a>
</div>
</div>
</template>
<script>
export default {
methods: {
logout() {
this.$router.push({ path: '/login' });
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.navbar {
transition: height 0.28s;
position: relative;
height: 70px;
line-height: 70px;
color: #717171;
background-color: #fff;
padding: 0 20px 0 30px;
border-radius: 0px !important;
box-shadow: 0 0 0 1px #f6f6f6;
.info-container {
height: 70px;
display: inline-block;
position: absolute;
right: 32px;
>a {
color: #999;
text-decoration: none;
&:hover {
color: #11a0e0;
}
}
}
}
</style>
# ./src/views/layout/Sidebar.vue
# 侧边菜单栏
<template>
<div class="menu">
<!-- 为了简单,这里的菜单不是从router读回来的 -->
<!-- 若要读回来,带上管理权限,请参考文后推荐的demo -->
<el-menu :default-active="onRoutes" class="el-menu-vertical-demo" unique-opened router theme="dark">
<el-menu-item index="/dashboard">工作台</el-menu-item>
<el-menu-item index="/hello">Hello页面</el-menu-item>
</el-menu>
</div>
</template>
<script>
export default {
name: 'Sidebar',
computed: {
onRoutes () {
return this.$route.path;
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss">
.menu {
overflow-y: auto;
height: 100%;
.el-menu {
background-color: transparent;
}
.el-menu-item {
background-color: transparent;
&.is-active {
background-color: #4e535f;
color: #fff;
}
}
}
</style>
# ./src/views/layout/index.js
# 快速引用
export { default as Navbar } from './Navbar';
export { default as Sidebar } from './Sidebar';
export { default as AppMain } from './AppMain';
再次访问http://localhost:8080/dashboard,发现整个后台的框架基本成型了,点击切换一下dashboard
与hello
页面。上面的文件代码也基本可以看懂的,这一部分就这样了。
(请继续阅读下篇http://www.jianshu.com/p/9baa03eb31fc)
学习是一条漫漫长路,每天不求一大步,进步一点点就是好的。
网友评论