Vue入门与进阶
2018-07-24 本文已影响3人
nimw
1. Vue概述
1.1 Vue介绍
Vue 是一套用于构建用户界面的渐进式框架。
1.2 Vue核心思想
- 双向数据绑定
Vue双向数据绑定利用了Object对象的set()和get()方法,原理如下:
<input type="text" id="userName" >
<span id="uName"></span>
<script>
const obj = {}
Object.defineProperty(obj, 'text', {
get: function(val) {
console.log('get init');
},
set: function(val) {
console.log('set:' + val);
ipt.value = val;
span.innerText = val;
}
})
const ipt = document.getElementById('userName');
const span = document.getElementById('uName');
ipt.addEventListener('keyup', function(e) {
obj.text = e.target.value;
})
</script>
1.3 Vue与React对比
1.3.1 不同点
一、Vue
- 简单的语法和项目构建。
二、React
- 适用于大型项目以及更好的可测试性。
- 更大的生态圈带来的更多的支持的工具。
1.3.2 相同点
- 虚拟DOM
- 轻量级
- 响应式
- 服务端渲染
- 易于集成路由工具、打包工具和状态管理工具
- 优秀的支持和社区
2. Vue基础
2.1 Vue环境搭建
2.1.1 环境构建方式
- 官方拷贝
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
-
npm
安装 -
vue-cli
工具构建
2.1.1 vue-cli工具构建SPA应用
npm i -g vue-cli
-
vue init webpack-simple demo
//初始化一个简单的webpack项目 -
vue init webpack demo
//初始化一个完整的webpack项目
2.2 Vue基础语法
- 模板语法
- Mustache语法:
{{msg}}
- Html赋值:
v-html = ""
- 绑定属性:
v-bind:id = ""
- 使用表达式:
{{ok ? 'YES' : 'NO'}}
- 文本赋值:
v-text = ""
- 指令:
v-if = ""
- 过滤器:
{{message | capitalize}}
和v-bind:id = "rawId | formtaId"
注释:vue
组件中的data
推荐使用方法的方式data(){return {}}
返回数据,这样不同组件实例之间就不会共用数据。
-
Class
和Style
绑定
- 对象语法
<div v-bind:class="{active: isActive, 'text-danger': hasError}">
- 数组语法
<div v-bind:class="[activeClass, errorClass]">
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
- style绑定
<div v-bind:style="{color: activeColor, fontSize: fontSize + 'px'}">
- 条件渲染
v-if
v-else
v-else-if
v-show
v-cloak
- 事件处理器
-
v-on:click="greet"
、@click="greet"
-
v-on:click.stop
、v-on:click.stop.prevent
、v-on:click.self
、v-on:click.once
v-on:keyup.enter/tab/delete/esc/space/up/down/left/right
- Vue组件
- 全局组件和局部组件
单页面应用一般使用的都是局部组件。 - 父子组件通讯-数据传递
//父组件
<template>
<div class="hello">
<Counter v-bind:num="num" v-on:incre="increment" v-on:decre="decrement"/>
<span>{{`parent: ${num}`}}</span>
</div>
</template>
<script>
import Counter from './Counter'
export default {
data () {
return {
num: 10
}
},
components: {
Counter
},
methods: {
increment() {
this.num++
},
decrement() {
this.num--
}
}
}
</script>
//子组件
<template>
<div>
<button @click="increment">+</button>
<button v-on:click="decrement">-</button>
<p><span>{{num}}</span></p>
</div>
</template>
<script>
export default {
props: ['num'],
methods: {
increment() {
this.$emit('incre');
},
decrement() {
this.$emit('decre')
}
}
}
</script>
Slot
2.3 路由 vue-router
2.3.1路由基础介绍
- 路由
根据不同的url地址展示不同的内容或页面。 - 后端路由
服务器根据url地址返回不同页面。 - 前端路由
不同路由对应不同内容或页面的任务交给前端来做。 - 前端路由使用场景
单页面应用。 - 前端路由的优缺点
优点:路由跳转用户体验好。
缺点:不利于SEO;浏览器前进后退重新发送请求,没有利用缓存;无法记住之前页面的滚动位置。 - vue-router介绍
vue-router官网
(1) 跳转组件
<router-link></router-link>
注释: router-link就是一个封装好的a标签,可以添加样式。
(2) js跳转
this.$router.push({path: ''})
(3) 展示组件
<router-view></router-view>
注释:vue-router
是对historyAPI
的封装。
2.3.2. 动态路由匹配
- 动态路由介绍
模式 | 匹配路径 | $route.params |
---|---|---|
/user/:username | /user/even | {username: 'even'} |
/user/:username/post/:post_id | /user/even/post/123 | {username: 'even', post_id: 123} |
- 动态路由的使用
//路由入口文件: scr/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import GoodList from '@/views/GoodList'
Vue.use(Router)
export default new Router({
mode: 'history', //默认值为hash
routes: [
{
path: '/goods/:goodsId/user/:userName',
name: 'GoodList',
component: GoodList
}
]
})
//src/views/GoodsList.vue文件
<template>
<div>
<div>这是商品列表页面</div>
<span>{{$route.params.goodsId}}</span>
<span>{{$route.params.userName}}</span>
</div>
</template>
<script>
export default {}
- 使用
vue-cli
工具构建的项目已经嵌套vue-router
。 - 路径完全匹配模式(
/goods/@@@/user/&&&
)才能够访问到该路由视图。 -
mode
默认取值为hash
,此时通过#
+ 路径才能访问到。如果取值为history
,则不用加#
。
2.3.3. 嵌套路由
- 嵌套路由的使用
//路由入口文件: scr/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import GoodList from '@/views/GoodList'
import Title from '../views/Title'
import Image from '../views/Image'
Vue.use(Router)
export default new Router({
mode: 'history', //默认值为hash
routes: [
{
path: '/goods',
name: 'GoodList',
component: GoodList,
children: [
{
path: 'title',
name: 'title',
component: Title
},
{
path: 'img',
name: 'img',
component: Image
}
]
}
]
})
//src/views/GoodsList.vue文件
<template>
<div>
<div>这是商品列表页面</div>
<router-link to="/goods/title">
显示标题子路由
</router-link>
<router-link to="/goods/img">
显示图片子路由
</router-link>
<div>
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {}
</script>
//src/views/Image.vue文件
<template>
<div>图片子路由</div>
</template>
<script>
export default {}
</script>
//src/views/Title.vue文件
<template>
<div>标题子路由</div>
</template>
<script>
export default {}
</script>
- 在
children
处注册路由时使用相对于父路由的路径即可,在router-link
的to
属性跳转地址要使用完整路径。 - 从子路由的用法可知,路由的本质并不是整个页面的显示/隐藏切换,而是
页面某个区域
的显示隐藏切换。
2.3.4. 编程式路由
- 编程式路由介绍
使用js实现页面的跳转。
$router.push("name")
$router.push({path: "name"})
$router.push({path: "name?a=123})
$router.push({path: "name", query: {a: 123}})
-
$router.go(1/-1)
注意: 对比区分query
参数的传递与动态路由params
参数的传递。 query传递的是?a=1;b=2
字段,通过$route.query.key
的方式取值。动态参数传递的是/a/b
字段,通过$route.params.key
的方式取值。
总结:①$route.query.key
获取的是当前url中query中的字段值,$route.params.key
获取的是当前url中params中的字段值。②使用router-link
组件跳转和js
跳转都可以传递params
和query
。
- 编程式路由的使用
//路由入口文件: scr/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import GoodList from '@/views/GoodList'
import Cart from '@/views/Cart'
Vue.use(Router)
export default new Router({
mode: 'history', //默认值为hash
routes: [
{
path: '/goods',
name: 'GoodList',
component: GoodList
},
{
path: '/cart',
name: 'cart',
component: Cart
}
]
})
//src/views/GoodsList.vue文件
<template>
<div>
<div>这是商品列表页面</div>
<button @click="toCart">
跳转到购物车
</button>
</div>
</template>
<script>
export default {
methods: {
toCart() {
this.$router.push({path: "/cart", query: {a: 1}})
}
}
}
</script>
//src/views/Cart.vue文件
<template>
<div>
<div>这是购物车页面</div>
<span>{{$route.query.a}}</span>
<button @click="backToGoods">
返回商品列表
</button>
</div>
</template>
<script>
export default {
methods: {
backToGoods() {
this.$router.go(-1)
}
}
}
</script>
2.3.5. 命名路由和命名视图
1.命名路由和命名视图介绍
给路由定义不同的名字,根据名字进行匹配。
给不同的router-view
定义名字,通过名字进行对应组件的渲染。
- 命名路由的使用
//路由入口文件: scr/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import GoodList from '@/views/GoodList'
import Cart from '@/views/Cart'
Vue.use(Router)
export default new Router({
mode: 'history', //默认值为hash
routes: [
{
path: '/goods',
name: 'GoodList',
component: GoodList
},
{
path: '/cart/:cartId',
name: 'cart',
component: Cart
}
]
})
//src/views/GoodsList.vue文件
<template>
<div>
<div>这是商品列表页面</div>
<router-link v-bind:to="{name: 'cart', params: {cartId:123}, query: {a:1}}">跳转到购物车页面</router-link>
</div>
</template>
<script>
export default {}
</script>
//src/views/Cart.vue文件
<template>
<div>
<div>这是购物车页面</div>
<span>{{$route.params.cartId}}</span>
<span>{{$route.query.a}}</span>
</div>
</template>
<script>
export default {}
</script>
- 命名视图
Vue Router文档-命名视图
2.4 请求数据
2.4.1 vue-resource
-
vue-resource
的请求API
是按照REST
风格设计的,它提供了7种请求API
:
get(url, [options])
head(url, [options])
delete(url, [options])
jsonp(url, [options])
post(url, [body], [options])
put(url, [body], [options])
patch(url, [body], [options])
- 发送请求时的
options
选项对象包含以下属性
参数 | 类型 | 描述 |
---|---|---|
url |
string |
请求的URL |
method |
string |
请求的HTTP 方法,例如:'GET ', 'POST '或其他HTTP 方法 |
body |
Object , FormData string
|
request body |
params |
Object |
请求的URL 参数对象 |
headers |
Object |
request header |
timeout |
number |
单位为毫秒的请求超时时间 (0 表示无超时时间) |
before |
function(request) |
请求发送前的处理函数,类似于jQuery 的beforeSend 函数 |
progress |
function(event) |
ProgressEvent 回调处理函数 |
credientials |
boolean |
表示跨域请求时是否需要使用凭证 |
emulateHTTP |
boolean |
发送PUT , PATCH , DELETE 请求时以HTTP POST 的方式发送,并设置请求头的X-HTTP-Method-Override
|
emulateJSON |
boolean |
将request body 以application/x-www-form-urlencoded content type 发送 |
- 全局拦截器
interceptors
Vue.http.interceptors.push((request, next) => {
// ...
// 请求发送前的处理逻辑
// ...
next((response) => {
// ...
// 请求发送后的处理逻辑
// ...
// 根据请求的状态,response参数会返回给successCallback或errorCallback
return response
})
})
- vue-resource使用示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vue-resource</title>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="../../node_modules/vue/dist/vue.js"></script>
<script src="../../node_modules/vue-resource/dist/vue-resource.js"></script>
</head>
<body>
<div id="app">
<h2>vue-resource演示</h2>
<a href="#" @click="sendGet">发送Get请求</a>
<a href="#" @click="sendPost">发送Post请求</a>
<a href="#" @click="sendJsonp">发送Jsonp请求</a>
<a href="#" @click="sendHttp">全局函数</a>
<p v-text="response"></p>
</div>
<script>
new Vue({
el:"#app",
data:{
response:''
},
http: {
root: 'http://localhost:8050/imoocmall/'
},
mounted() {
Vue.http.interceptors.push((request, next) => {
console.log('request init.');
next((response) => {
console.log('response init.');
return response
})
})
},
methods:{
sendGet() {
this.$http.get('package.json',{
params:{
userId: "101",
},
headers:{
access_token:"abc"
}
}).then(res => {
this.response = res.data;
}).catch(err => {
this.response = err;
});
},
sendPost() {
this.$http.post('package.json', {
userId: '102'
}, {
headers: {
access_token:"abcd"
}
}).then(res => {
this.response = res.data;
}).catch(err => {
this.response = err;
});
},
sendJsonp(){
this.$http.jsonp("http://www.imooc.com/course/ajaxskillcourse?cid=796",{
params:{
userId:"1001"
}
}).then(res => {
this.response = res.data;
}).catch(err => {
this.response = err;
})
},
sendHttp() {
this.$http({
url:"package.json",
method:"GET",
params:{ userId:"103" },
headers:{ token:"123" },
timeout:50,
before() {
console.log("before init")
}
}).then(res => {
this.response = res.data;
});
}
}
});
</script>
</body>
</html>
注释: ①引入 vue-resource
之后可以通过this.$http
的方式使用。
2.4.2 Axios
- Axios简介
Axios
是一个基于promise
的HTTP
库,可以用在浏览器和node.js
中。 - 请求方法介绍
axios.request(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
-
axios.patch(url[, data[, config]])
注意: ·Axios· 请求方法中没有jsonp
请求。
- 执行
get
请求
// 为给定 ID 的 user 创建请求
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// 可选地,上面的请求可以这样做
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
- 执行 POST 请求
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
- 执行多个并发请求
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// 两个请求现在都执行完成
}));
- Axios使用示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>axios</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="../../node_modules/vue/dist/vue.js"></script>
<script src="../../node_modules/axios/dist/axios.js"></script>
</head>
<body>
<div id="app">
<h2>vaxios演示</h2>
<a href="#" @click="sendGet">发送Get请求</a>
<a href="#" @click="sendPost">发送Post请求</a>
<a href="#" @click="sendHttp">全局函数</a>
<p v-text="response"></p>
</div>
<script>
new Vue({
el:"#app",
data:{
response:''
},
mounted() {
axios.interceptors.request.use((req) => {
console.log('request init.');
return req;
});
axios.interceptors.response.use((res) => {
console.log('response init.');
return res;
});
},
methods:{
sendGet() {
axios.get('../../package.json',{
params:{
userId: "101",
},
headers:{
token:"abc"
}
}).then(res => {
this.response = res.data;
}).catch(err => {
this.response = err;
});
},
sendPost() {
axios.post('../../package.json', {
userId: '102'
}, {
headers: {
token:"abcd"
}
}).then(res => {
this.response = res.data;
}).catch(err => {
this.response = err;
});
},
sendHttp() {
axios({
url:'../../package.json',
method:"POST",
data:{ userId:"103" },
headers:{ token:"123" }
}).then(res => {
this.response = res.data;
});
}
}
});
</script>
</body>
</html>
注释:①axios
的参数传递方式与vue-resource
基本相同。② 注意区分get
请求与post
请求的参数传递方式。
2.4 Vue进阶
2.4.1 模拟mock数据
在vue
开发过程中,有时需要使用本地json
模拟后台接口数据,测试前端页面展示情况。对于使用vue-cli
工具构建的项目,封装了express
框架,我们可以通过拦截请求的方式使用mock
数据。
- 创建
mock
数据json
文件 - 在
webpack.dev.conf.js
文件中拦截请求
//imoocmall/build/webpack.dev.conf.js文件
var goodsData = require('../mock/goods.json')
devServer: {
before (app) {
app.get('/goods', function (req, res) {
res.json(goodsData);
})
},
//...
}
//..
注释: 这里的app.get('/goods', (req, res) => {})
就是express
框架定义后端路由接口的写法。
- 使用mock数据
axios.get('/goods',)
.then(res => {
//...
})
2.4.2 图片懒加载
使用vue-lazyload插件可以实现图片懒加载。
- 安装
npm i vue-lazyload -d
- 引入
main.js
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload, {
error: 'dist/error.png',
loading: 'dist/loading.gif',
})
- 使用
<img v-lazy="'/static/'+good.productImage" alt="">
2.4.3 请求代理
- 开发过程中,前端服务与后端接口一般存在着跨域问题。
vue-cli
提供了proxyTable
代理功能解决跨域问题。
注释:① 开发环境前端服务端口8080(/config/index.js中的port: 8080
)与后端服务端口(/server/bin/www中的var port = normalizePort(process.env.PORT || '3000');
)不同,存在跨域,所有需要使用请求代理。② 一般仅在开发环境中配置。
注意:①跨域问题只是web
前端浏览器的行为,在web
前端请求不符合同源策略接口数据时出现。②后端node
连接mongodb
数据库即使协议域名端口不同(不符合同源策略),也不存在跨域问题。 - 修改
/config/index.js
文件中的dev.proxyTable
配置
proxyTable: {
'/goods': {
target: 'http://localhost:3000'
}
}
}
此时,当我们请求 http://localhost:8888/goods
的时候,就等于请求了http://localhost:3000/goods
。
proxyTable: {
'/api': {
target: 'http://localhost:3000' ,
pathRewrite: {
'^/api': ''
}
}
}
}
此时,当我们请求 http://localhost:8888/api
的时候,就等于请求了http://localhost:3000
。