邂逅Vue.js
1.编程范式
- 命令式编程 如jquery
- 声明式编程 只需要生命需要显示的东西就可以
2. 创建Vue实例的时候,传入了一个对象options,这个opentions中包含了
- el: 类型:string|HTMLElement 作用:决定之后Vue实例会管理哪一个DOM
- data: 类型:Object|Function(组件中data必须是一个函数) 作用:Vue实例对应的数据对象
- methods: 类型:{[key: string] : Function} 作用: 定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用。
Vue基础语法
- 前端代码缩进最好两个空格,多数大型开源框架都是两个
模板语法
Mustache语法({{}})
- 可以写变量,也可以写简单的表达式
{{message}} {{firstName + ' ' + lastName}}
指令
- v-once
- 该指令后面不需要跟任何表达式
- 该指令表示元素和组件只渲染一次,不会随数据的改变而改变<h2 v-once>{{message}}<h2>
- v-html
- 该指令后面往往会跟上一个string类型
- 会将string的html解析出来并且进行渲染<h2 v-html='url'><h2> data:{ url: '<a href="http://www.baidu.com">百度一下<a>' }
- v-text
一般不用,不够灵活,会覆盖innerHTML内容{{message}} <h2 v-text='message'><h2>
- v-pre , 原生显示
<h2 v-pre>{{message}}</h2>
- v-cloak
- 在vue解析之前,div中有一个属性v-cloak
- 在vue解析之后,div中的v-cloak属性被删除
- 主要是用来防止界面上显示未渲染的代码<div id='app'>{{message}}<div> <script> new Vue({ el: '#app', data{ message: 'aaa' } }) </script> <style> [v-cloak] { display: none } </style>
- v-bind
- 动态绑定属性
- 缩写: ‘:’
- 预期: any(with argument) | Object(without argument)
- 参数: attrOrProp(optional)<img v-bind:src='imgUrl'> 语法糖写法 <img :src='imgUrl'> <h1 :class = '{class1(类名): showClass1(bool值), class2: showClass2}'><h1> <h1 :class = 'getClass()'><h1> methods:{ getClass: function(){ return {class1: showClass1, class2: showClass2} } }
- v-bind:style
- 动态绑定样式
-<h2 :style="{fontSize: '50px'}"></h2>
-<h2 style="{fontSize: finalSize + 'px'}"></h2>
计算属性
- 数据展示前需要进行处理时使用。
computed:{ fullName: function(){ return this.firstName + this.lastName } }
- 计算属性会缓存,多次调用只会计算一次,方法不会缓存
- 计算属性的getter和setter(set不常用)
computed: { fullName: { get: function(){ return this.firstName + ' ' + this.lastName }, set: function(name){ this.fullName = name } } }
对象字面量增强写法
// 普通写法
name = 'aaa'
age = 18
const obj = {
name: name,
age: age
}
// 增强写法
const obj = {
name,
age
}
事件监听
- v-on
- 作用: 绑定事件监听器
- 缩写: @
- 预期: Function| Inline Statement | Object
- 参数: event - 参数传递
- 如果方法不需要额外参数,那么方法后的()可以不添加
- 如果方法本身有参数,但是调用的时候没有传递,省略小括号,默认会将event参数传递进去
- 如果同时需要传入某个参数,同时需要event,可以通过$event传入事件<button @click="btnClick"></button> 默认传过去event <button @click="btnClick()"></button> 默认传过去undefined <button @click="btn1Click('aaa',$event)"></button> methods:{ btnClick(aaa){ console.log(aaa) }, btn1Click(aaa, event){ console.log(aaa) } }
- v-on修饰符的使用
- .stop 阻止冒泡<div @click='divClick'> <button @click.stop="btnClick"></button> </div>
- prevent 阻止默认事件
<form action="baidu" @click.prevent="submitClick"> </form> # 阻止默认提交事件
- .{keyCode | keyAlias} 只当事件是从特定键触发时才触发回调 - .native 监听根元素的原生事件 - .once 只触发一次回调
条件判断
- v-if
- v-else-if
- v-else
- vue内部会复用dom元素,如果不想让复用,可以加个key
- v-show
- v-show dom会渲染,使用样式决定是否显示
- v-if 根据条件判断是否要渲染
- 频繁切换使用v-show
循环
- v-for遍历
官方推荐在使用v-for遍历时,给对应的元素或组件加上一个:key属性
为什么要使用key属性?
假设有一个数组,现在想在数组中间插入一个元素,如果没有key,则插入位置以及后面的位置都需要改变,而如果有key,则会先使用key与元素对应起来,插入时只修改插入位置即可。key必须与item一一对应,所以不能用index。
- 使用key给每一个节点做一个唯一标识
- Diff算法可以正确的识别此节点
- 找到正确的位置插入新的节点
key的作用主要是为了更高效的更新虚拟DOM
- 哪些数组的方法是响应式的?
- push(): 可以添加多个 arr.push(‘aaa’, ‘bbb’, ‘ccc’)
- pop(): 删除数组的最后一个元素
- shift() : 删除数组第一个元素
- unshift(): 在数组前面添加元素, 可以添加多个
- sort():
- reverse()
- splice() : 删除元素/插入元素/替换元素 会影响原始数组
第一个参数start,表示开始操作的索引
删除元素: 第二个参数表示要删除几个元素,如果没有传,表示删除所有
替换元素: 第二个参数表示要替换的个数,后面参数表示要替换的元素
插入元素: 第二个参数为0,后面参数为添加的元素arr = [1,2,3,4,5] arr.splice(1) => [2,3,4,5] arr = [1,2,3,4,5] arr.splice(1,2)⇒ [2,3] arr = [1,2,3,4,5] arr.splice(1,2,'m','n', 'o') => [1, "m", "n", "o", 4, 5] arr = [1,2,3,4,5] arr.splice(1,0,'m', 'n')==> [1, "m", "n", 2, 3, 4, 5]
通过数组索引直接修改元素不是响应式的, 直接修改界面不会更新
- Vue.set(obj, index, newValue)
Vue.set(arr, 0, ''a)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h2 v-show="books.length==0">购物车为空</h2>
<table v-show="books.length!=0">
<tr>
<th>书籍名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</tr>
<tr v-for="(book, index) in books" :key='book.bookName'>
<td>{{book.bookName}}</td>
<td>{{book.publishDate}}</td>
<td>{{book.price | transPrice}} </td>
<td><button @click="increment(index)">+</button>{{book.number}}<button @click="decrement(index)">-</button></td>
<td><button @click="remove(index)">移除</button></td>
</tr>
</table>
<p>总价:¥{{totalPrice | transPrice}}</p>
</div>
</body>
</html>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
books: [
{'bookName': '《算法导论》', 'publishDate': '2006-9', 'price': 85.00, 'number': 0},
{'bookName': '《UNIX变成艺术》', 'publishDate': '2006-2', 'price': 59.00, 'number': 0},
{'bookName': '《编程珠玑》', 'publishDate': '2008-10', 'price': 39.00, 'number': 0},
{'bookName': '《代码大全》', 'publishDate': '2006-12', 'price': 128.00, 'number': 0}
],
},
computed: {
totalPrice(){
let result = this.books.reduce(function(preValue, book){
return preValue + book.price * book.number
}, 0)
return result;
}
},
methods:{
increment(index, book){
this.books[index].number--;
},
decrement(index, book){
this.books[index].number--;
},
remove(index){
this.books.splice(index,1);
}
},
filters: {
transPrice(price){
if(typeof(price)=='number'){
return price.toFixed(2);
}
}
}
})
</script>
- 高阶函数
- filter() : filter回调函数必须返回一个bool值
- map()
- reduce(): 对数组中所有内容进行汇总arr = [1,2,3,4] arr.reduce(function(preValue, n){ return preValue + n; }, 0) 0 表示preValue的初始值, n表示每次遍历时的元素
- 表单绑定v-model
- v-model其实是一个语法糖,他的背后本质包含两个操作
- v-bind绑定一个value属性
- v-on指令给当前元素绑定input事件<input type='text' v-model='message'> 相当于 <input type='text', :value='message' @input='message= $event.target.value'> 相当于 <input type='text' value='message' @input='changeMessage'> methods:{ changeMessage(event){ this.message = event.target.value; } }
- v-model结合radio
<label for="male"> <input type="radio" id="male" value="男" v-model='sex'>男 </label> <label for="female"> <input type="radio" id="female" value="女" v-model='sex'>女 </label> data{ sex: '男' }
- v-model结合checkbox
<!-- 单选 --> <label for="license"> <input type="checkbox" id="license" v-model='isAgree'>同意协议 </label> <button :disabled='!isAgree'>下一步</button> data{ isAgree: false } <div> <!-- 多选 --> <input type="checkbox" value="篮球" v-model="hobbies">篮球 <input type="checkbox" value="足球" v-model="hobbies">足球 <input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球 <input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球 </div> data{ hobbies: [] }
- v-select :单选(字符串) 多选(数组)
- v-model 修饰符
- .lazy<input type="text" v-model.lazy="message">
敲回车或失去焦点才同步数据
- .number<input type="number" v=model.number="age">
- .trim<input type="text" v-model.trim="message">
组件化开发
组件注册步骤
- 创建组件构造器对象
- 注册组件
- 使用组件
const cpn = Vue.extend({ template: `<div><h2>hhhhh</h2></div>` }) Vue.components('my-cpn', cpn)
- 全局组件和局部组件
- 全局组件可以在多个Vue实例中使用
- 局部组件const app = new Vue({ el: '#app', data:{} conponents: { cpn: cpn #局部组件 } }
- 父组件和子组件
- 注册组件语法糖, 主要时省去了调用Vue.extend()的步骤,而是直接可以使用一个对象来代替
// 全局 Vue.components('cpn', { template: `<div><h2>hhhhh</h2></div>` })
组件间的通信
父子组件的通信
- 通过props向子组件传递数据
<div id="app">
<cpn :cmovies="movies"></cpn>
</div>
<template id="cpn">
<div>
<ul>
<li v-for="movie of cmovies" :key="movie">{{movie}}</li>
</ul>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
movies: ['aaa', 'bbb', 'ccc'],
message: 'hello world'
},
components: {
'cpn': {
template: '#cpn',
data(){
return{
}
},
// props: ['cmovies']
// props:{
// cmovies: Array
// }
props:{
cmovies: {
type: Array,
default(){
return [1,2,3]
}
required: true
}
}
}
}
})
</script>
props里面如果是驼峰式写法,在v-bind用的时候要使用-连接
cMovies —> :c-movies=“movies”
2. 通过事件向父组件发送消息
- 在子组件中,通过$emit()来触发事件
- 在父组件中,通过v-on来监听子组件事件
<div id="app">
<cpn :cmovies="movies" @item-click="cpnClick"></cpn>
</div>
<template id="cpn">
<div>
<button v-for="movie of cmovies" :key="movie" @click="itemClick(movie)">{{movie}}</button>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
movies: ['aaa', 'bbb', 'ccc'],
message: 'hello world'
},
methods:{
cpnClick(item){
console.log(item);
}
},
components: {
'cpn': {
template: '#cpn',
data(){
return{
}
},
methods: {
itemClick(item){
this.$emit('item-click', item)
}
}
}
}
})
</script>
父子组件的访问方式
- 父组件访问子组件:使用$children或$refs
this.$children 是一个数组,包含了所有的子组件<child refs='aaa'></child> 在父组件中使用this.$refs.aaa获取对应子组件
- 子组件访问父组件:使用$parent
- 访问根组件$root
插槽(slot)
- 组件的插槽是为了让封装的组件更加有扩展性
- 让使用者可以决定组件内部的一些内容到底展示什么
- 插槽的基本使用
<slot></slot>
- 插槽的默认值
<slot><button>按钮</button></slot>
- 如果有多个值,同时放到组件中替换,一起作为替换元素
具名插槽
<cpn>
<button slot='left'><button>
<cpn>
<template id='cpn'>
<slot name='left'></slot>
<slot name='center'></slot>
<slot name='right'></slot>
</template>
作用域插槽
父组件替换插槽的标签,但是内容由子组件来提供
<div id="app">
<cpn>
<template slot-scope='slot'>
<span>{{slot.data.join('-')}}</span>
</template>
</cpn>
<cpn>
<template slot-scope='slot'>
<span>{{slot.data.join('*')}}</span>
</template>
</cpn>
</div>
<template id="cpn">
<div>
<slot :data='languages'>
<ul>
<li v-for="item of languages">{{item}}</li>
</ul>
</slot>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: '#app',
data(){
return {
}
},
components: {
cpn: {
template: '#cpn',
data(){
return {
languages: ['aaa', 'bbb', 'ccc', 'ddd']
}
}
}
}
})
</script>
模块化开发
模块化有两个核心:导出和导入
CommonJs的导出
moduleA.exports = {
flag: true,
sum(a,b){
return a + b
}
}
CommonJS的导入
let {flag, sum} = require(‘moduleA’)
webpack入门
Vue Cli详解
安装
npm install -g @vue/cli
初始化项目
- Vue cli3
vue create projectName
- 兼容Vue cli2
npm install @vue/cli-init -g
vue init webpack projectName
Vue CLI2使用
vue init webpack vuecli2test
输入上述命令后有以下配置:
- Project name : 项目名称,不能包含大写
- Project description
- Author: 作者信息,默认会从git中读取
- Vue build : runtime + complier和runtime-only
- Install vue-router : 是否安装路由
- 其他不重要的
chrome使用v8引擎直接将js代码转换成二进制文件,所以运行速度较快
node环境可以直接执行js文件
node test.js
static文件夹和assets文件夹的区别?
都是存放静态文件的,assets中的图片会根据设置的limits,将大于的使用hash重命名后放到dist下对应文件夹中,小于的转换成base64格式直接显示。而static中的文件,webpack不会进行处理,会原样显示。
runtime+complier和runtime-only的区别
//runtime+complier
import App from './App'
new Vue({
el: '#app',
template: '<App/>'
})
template-->ast(抽象语法树)-->render-->virtualDom-->realDom
//runtime-only (性能更高,代码更少(少了complier那部分))
new Vue({
el: '#app',
render: h=>h(App)
})
h实际是createElement函数
普通用法: createElement(标签, {属性}, [内容])
createElement('h2', {class: 'box', ['hello world']})
也可以直接传入组件: createElement(App)
render-->virtualDom-->realDom
runtime-only模式不包含template,那么app中的template是如何处理的?使用vue-template-complier将template编译成render函数后才使用
Vue Cli3
vue-cli3和vue-cli2的区别
- vue-cli3是基于webpack4打造,vue-cli2还是webpack3
- vue-cli3的设计原则是“0配置”,移除了配置文件目录下的build和config等目录
- vue-cli3提供vue ui命令,提供了可视化配置,更加人性化
- 移除了static文件夹,新增了public文件夹,并且index.html移动到public中
Vue Cli3修改配置的方法
-
vue ui启动一个本地服务,在界面上修改
-
node_modules下面的@vue/webpack.config.js
-
在当前项目下创建一个文件,vue.config.js
module.exports = { }
vue-router
Vue-router认识路由前端渲染与后端渲染url的hash和HTML5的historyvue-router的基本使用路由的懒加载vue-router嵌套路由Vue-router参数传递Vue-router导航守卫keep-alive
Vue-router
认识路由
-
概念
路由(routing)就是通过互联的网络把信息从源地址传输到目的地址的活动
-
路由器提供了两种机制:路由和转送
-
路由是决定数据包从来源到目的地的路径
-
转送将输入端的数据转移到合适的输出端
-
-
路由表
-
路由表本质上就是一个映射表,决定了数据包的指向
前端渲染与后端渲染
-
后端路由
- 后端处理URL与页面间的映射关系
-
前后端分离
-
后端只负责提供数据,不负责任何界面的内容
-
前端渲染:浏览器中显示的网页中的大部分内容,都是由前端写的js代码在浏览器中执行,最终渲染出来的网页
-
后端只提供 API来返回数据,前端通过Ajax返回数据,通过js将数据渲染到页面中
-
-
单页面富应用(SPA页面)
-
在前后端分离基础上,又加上了一层前端路由。区别
-
整个网页只有一个html页面
-
改变url,但是页面不进行整体的刷新
-
url的hash和HTML5的history
-
url的hash
-
url的hash也就是锚点(#),本质上是改变window.location的href属性
-
可以通过直接赋值location.hash来改变href,但是页面不发生刷新
-
location.hash = "aaa" url变为"localhost:8080/aaa"</pre>
-
-
HTML5的history模式
- pushState
history.pushState({},'',home) // localhost:8080/home history.pushState({},'',about) // localhost:8080/about history.pushState({},'',mine) // localhost:8080/mine //以上相当于入栈 history.back() //后退,相当于出栈 history.forward() //前进,相当于压栈</pre>
- replaceState(): 替换url,back方法失效
- pushState
vue-router的基本使用
- 安装
npm install vue-router --save
-
使用
-
导入路由对象,并且调用Vue.use(VueRouter)
-
创建路由实例,并传入路由映射配置
-
在Vue实例中挂载创建的路由实例
-
-
配置路由映射关系
-
创建路由组件
-
配置url和组件的映射关系
-
使用路由,通过 和
<router-link to="/home"></router-link> <router-link to="/about"></router-link> <router-view></router-view></pre>
-
-
路由的默认路径
path: '', redirect: '/home' }]
-
默认是hash模式,可以修改成history模式
routes; mode: 'history' })
-
router-link的属性
-
to:由于指定跳转的路径
-
tag:默认router-link会渲染成a标签,如果想渲染成别的标签,则可以使用此属性
-
replace:replace不会留下history记录,前进后退无法使用,相当于replaceState()
-
active-class: 激活的router-link默认会有一个router-link-active的类,如果不想使用默认类,则可以使用这个属性
-
//如果修改active-class,如果有多个router-link可能需要多处修改,也可以修改一个地方 const router = new VueRouter({ routes; mode: 'history', linkActiveClass: 'active' })
-
-
通过代码修改路由
this.$router.push('/home')
可以使用返回前进按钮this.$router.replace('/home')
前进后退不能用 -
动态路由
export default { name: '#app', data(){ return { userId: 'zhangsan' } } } { path: '/user/:userId', component: User } //获取参数 this.$route.params.userId {{$route.params.userId}}
路由的懒加载
-
npm run build后生成3个js文件 app.js manifest.js vender.js
app.js中是自己写的业务代码
manifest.js是为了打包的代码做底层支撑
vender.js中的是使用的第三方包
-
为什么要懒加载?
-
打包构建应用时,js文件比较大,会影响页面加载
-
如果能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样更加高效
-
-
如何进行路由懒加载
import Home from '@/components/Home' import About from '@/components/about' Vue.use(VueRouter) const routes = [ { path: '/home', component: Home }, { path: '/about', component: About } ] //使用路由懒加载 const Home = ()=>import('@/components/Home') const About = ()=>import('@/components/About') const routes = [ { path: '/home', component: Home }, { path: 'about', component: About } ]
vue-router嵌套路由
-
什么是嵌套路由?
-
比如在Home页面,希望通过/home/news和/home/message访问一些内容
-
一个路径映射一个组件,访问这两个路径也会分别渲染两个组件
-
-
如何实现嵌套路由?
-
创建对应的子组件,并在路由映射中配置对应的子路由
-
在组件内部使用标签
{ path: '/home', component: Home, children: [ path: 'news', //前面不要加/ components: News ] } ]
-
Vue-router参数传递
-
传递参数主要有两种类型params和query
-
params的类型
-
配置路由格式:/router/:id
-
传递的方式:在path后面跟上对应的值
-
传递后形成的路径:/router/123, /router/abc
<router-link :to="'/profile' + userId"></router-link>
-
-
query的类型
-
配置路由格式:/router
-
传递的方式:对象中使用query的key作为传递方式
-
传递后形成的路径:/router?id=123, /router?id=abc
-
<h2>{{$route.query.name}}</h2> //取参
-
Vue-router导航守卫
-
什么是导航守卫?
-
vue-router提供的导航守卫主要用来监听路由的进入和离开
-
vue-router提供了beforeEach和afterEach的钩子函数,他们会在路由改变前和路由改变后触发
-
-
一个小需求,根据路由的变化修改title值
path: '/home',
component: Home,
children: [...],
meta: {
title: '首页'
},
path: '/about',
component: About,
meta: {
title: '关于
}
]
const router = new VueRouter({
routes,
mode: 'history',
linkActiveClass: 'active
})
//前置守卫, 必须调用next方法 全局守卫
router.beforeEach((to, from, next)=>{
document.title = to.meta.matched[0].title
next()
})
/*
导航钩子的三个参数:
to: 即将要进入的目标的路由对象
from: 当前导航即将要离开的路由对象
next: 调用该方法后,才能进入下一个钩子 */
router.afterEach((to, from)=>{})
-
除了全局守卫,还有路由守卫和组件内守卫
-
const routes = [ { path: '/home', component: Home, beforeEnter(to, from ,next){ } } ]
-
keep-alive
-
keep-alive是Vue内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染
-
include- 字符串或正则表达式, 只有包含的组件会被缓存
-
exclude- 字符串或正则表达式, 排除的组件不会被缓存
-
<keep-alive exclude="User, About"> <router-view/> </keep-alive>
-
-
router-view也是一个组件,如果直接被包含在keep-alive里面,所有路径匹配到的试图组件都会被缓存
-
只有被keep-alive包围时,actived和deactived方法才会被调用
Vuex详解
-
Vuex是一个为Vue.js应用程序开发的状态管理模式
- 采用集中存储管理应用的所有组件的状态
-
管理什么状态?
- 多个页面间共享的,如用户登录状态,用户名称,头像,地理位置信息,商品的收藏,购物车中的商品
-
//router/index.js import Vuex from 'vuex' Vue.use(Vuex) // 安装插件,会去调用插件的install方法 const store = new Vuex.store({ state: { counter: 0 }, mutations: { increment(state){ //默认有一个参数state state.counter++ }, decrement(state){ state.counter-- } }, actions: { }, getters: { }, modules: { } }) export default store //组件中调用 <button @click="add"></button> methods: { add(){ this.$store.commit('increment') } }
-
Vuex是一个全局的单例模式
Vuex核心概念
-
State
- 单一状态树:所有状态信息都保存在一个store中
-
Getters
-
当需要从store中获取一些state变异后的状态时使用
-
const store = new Vuex.store({ state: { counter: 0 }, getters: { powerCounter(state){ return state.counter**2 } } } //使用 {{$state.getters.powerCounter}}
-
const store = new Vuex.store({ state: { counter: 0, students: [ {name: 'aaa', age:10}, {name: 'bbb', age:20}, {name: 'ccc', age:30}, {name: 'ddd', age:40}, ] }, getters: { powerCounter(state){ return state.counter**2 }, //获取大于20岁的学生 more20Stus(state){ return state.students.filter(s=>s.age>20) }, //获取大于20岁的学生的个数 (getters可以作为参数传入) more20StusLen(state, getters){ return getters.more20Stus.length }, //如果想自定义传入age moreAgeStus(state){ return function(age){ return state.students.filter(s=>s.age>age) } } } } //使用 {{$state.getters.powerCounter}} {{$state.getters.more20Stus}} {{$state.getters.more20StusLen}} {{$state.getters.moreAgeStus(30)}}
-
-
Mutation
-
Vuex的store状态更新的唯一方式:提交Mutation
-
Mutation主要包括两部分
- 字符串的事件类型(type)
- 一个回调函数(handler),该回调函数的第一个参数就是state
-
传递参数
- 参数被称为mutation的载荷(payload)
-
//router/index.js import Vuex from 'vuex' //普通提交风格 Vue.use(Vuex) // 安装插件,会去调用插件的install方法 const store = new Vuex.store({ state: { counter: 0 }, mutations: { incrementCount(state, count){ state.counter+=count } } }) export default store //组件中调用 <button @click="add"></button> methods: { add(){ this.$store.commit('incrementCount', count) } } //特殊提交风格 const store = new Vuex.store({ state: { counter: 0 }, mutations: { incrementCount(state, payload){ state.counter += payload.count } } }) export default store //组件中调用 <button @click="add"></button> methods: { add(){ this.$store.commit({ type: 'incrementCount', count }) } }
-
mutation响应规则
- 提前在store中初始化好所需的属性
- 当给state的对象添加新属性时,使用Vue.set(obj, key, value),当删除属性时,使用Vue.delete(obj, key)
-
Mutation常量
-
//router/index.js import Vuex from 'vuex' import {INCREMENT, DECREMENT} from 'mutation-types' Vue.use(Vuex) // 安装插件,会去调用插件的install方法 const store = new Vuex.store({ state: { counter: 0 }, mutations: { [INCREMENT](state){ //默认有一个参数state state.counter++ }, [DECREMENT](state){ state.counter-- } }, }) export default store //组件中调用 import {INCREMENT} from 'mutation-types' <button @click="add"></button> methods: { add(){ this.$store.commit(INCREMENT) } } //mutation-types.js export const INCREMENT = 'increment' export const DECREMENT = 'decrement'
-
-
VueX要求Mutation中的方法必须是同步方法
- 主要原因是使用devtools时,devtools可以帮助我们捕捉mutation的快照
- 但是如果是异步操作,那么devtools不能很好地追踪这个操作什么时候会被完成
- 不要在mutation中进行异步操作
-
-
Action
-
Action类似于Mutation,是用来代替Mutation进行异步操作的
-
//router/index.js import Vuex from 'vuex' import {INCREMENT, DECREMENT} from 'mutation-types' Vue.use(Vuex) // 安装插件,会去调用插件的install方法 const store = new Vuex.store({ state: { info: {name: 'aaa', age: 18} }, mutations: { updateInfo(state){ state.info = 'bbb' } }, actions: { aupdateInfo(context){ //默认携带context参数 return new Promise((resolve, reject)=>{ setTimeout(()=>{ context.commit('updateInfo') }, 1000) resolve() }) } } }) export default store //组件中调用 <button @click="add"></button> methods: { update(){ this.$store.dispatch('aupdateInfo').then(()=>{console.log('修改完成')}) //不带参数 this.$store.dispatch('aupdateInfo', payload) //携带参数 } }
-
-
-
Module
- Vue使用单一状态树,这样当应用变得复杂时,store对象可能变得非常臃肿,为了解决这个问题,Vuex允许我们将store分割成模块,每个模块拥有自己的state、mutations、actions、getters等
网络封装
axios
-
Vue发送网络请求有非常多的方式,如何选择?
- 传统的Ajsx是基于XMLHttpRequest(XHR)
- 配置和调用方式非常混乱
- jQuery-Ajax
- 为了一个网络请求,引用jQuery,得不偿失
- Vue1.x,有个Vue-resource
- Vue2.0已经去掉,不会再更新
- 传统的Ajsx是基于XMLHttpRequest(XHR)
-
为什么axios
- 在浏览器中发送XMLHttpRequests请求
- 在node.js中发送http请求
- 支持Promise API
- 拦截请求和响应
- 转换请求和响应数据
-
axios请求方式
-
axios(config)
-
axios({ url: 'http://123.207.32.32:8000/home/multidata', method: 'get', //默认get请求 }).then((res)=>{ console.log(res) })
-
axios({ url: 'http://123.207.32.32:8000/home/multidata', method: 'get', //默认get请求 params: { //get请求的参数,不用放在url后面用?拼接了 type: 'pop', page: 1 } }).then((res)=>{ console.log(res) })
-
-
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发送并发请求
-
axios.all([axios(), axios()]).then(results => {}) //可以将结果展开 axios.all([axios(), axios()]).then(axios.spread((res1, res2) => {}))
-
-
全局配置
-
//公共的配置 axios.defaults.baseUrl = "http://localhost:8080" axios.defaults.timeout = 5000 //超时时间5秒
-
常见的配置
- 请求地址:
url: '/user'
- 请求类型:
method: 'get'
- 根路径:
baseUrl: 'http://localhost:8080'
- 请求前的数据处理:
transformRequest: [function(data){}]
- 请求后的数据处理:
transformResponse: [function(data){}]
- 自定义请求头:
headers: {'x-Requested-With': 'XMLHttpRequest'}
- URL查询对象:
params: {id: 12}
- 查询对象序列化函数:
paramsSerializer: fucntion(params){}
- request body:
data:{key: 'aa'}
- 超时设置:
timeout: 10000
- 跨域是否带token:
withCredentials: false
- 自定义请求处理:
adapter: function(resolve, reject, config){}
- 身份验证信息:
auth: {uname: '', pwd: '1243'}
- 响应的数据格式:
responseType: 'json/blob/document/arraybuffer/text/stream'
- 请求地址:
-
-
axios实例
-
import axios from 'axios' const instance = axios.create({ bseURL: 'http://localhost:8080', timeout: 5000 }) //使用 instance({ url: '/user' }).then((res)=>{})
-
-
拦截器
const instance = axios.create({ baseURL: 'http://localhost:8080', timeout:5000 }) //拦截某个实例 //请求拦截 instance.interceptors.request.use(config=>{ console.log('请求成功拦截') return config }, err=>{ console.log('请求失败拦截') return err }) //响应拦截 instance.interceptors.response.use(response=>{ console.log('响应成功拦截') return response.data },err=>{ console.log('响应失败拦截') return err }) //如果要全局拦截,直接用axios.interceptors就可以