1丶VUEX
Vuex
什么是Vuex?
官方说法:Vuex 是一个专为 Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
个人理解:Vuex是用来管理组件之间通信的一个插件
为什么要用Vuex?
我们知道组件之间是独立的,组件之间想要实现通信,我目前知道的就只有props选项,但这也仅限于父组件和子组件之间的通信。如果兄弟组件之间想要实现通信呢?嗯..,方法应该有。抛开怎么实现的问题,试想一下,当做中大型项目时,面对一大堆组件之间的通信,还有一大堆的逻辑代码,会不会很抓狂??那为何不把组件之间共享的数据给“拎”出来,在一定的规则下管理这些数据呢? 这就是Vuex的基本思想了。
Vuex有什么特性?
怎么使用Vuex?
单向数据流指只能从一个方向来修改状态。下图是单向数据流的极简示意:(View->Actions->Mutations->State>View)
引入Vuex.js文件
创建实例:
new Vuex.Store({
state:{
//存放组件之间共享的数据
name:"jjk"
},
mutations:{
//显式的更改state里的数据
},
getters:{
//获取数据的方法
},
actions:{
//
}
});
先解释上面代码的意思:
new Vuex.Store({}) 表示创建一个Vuex实例,通常情况下,他需要注入到Vue实例里. Store是Vuex的一个核心方法,字面上理解为“仓库”的意思。Vuex Store是响应式的,当Vue组件从store中读取状态(state选项)时,若store中的状态发生更新时,他会及时的响应给其他的组件(类似双向数据绑定) 而且不能直接改变store的状态,改变状态的唯一方法就是,显式地提交更改(mutations选项)
他有4个核心选项:state mutations getters actions (下文会仔细分析)
这是上面代码:
那如何获取到state的数据呢?
一般会在组件的计算属性(computed)获取state的数据(因为,计算属性会监控数据变化,一旦发生改变就会响应)
import store from 'vuex'
export default {
name: "footer",
created(){
console.log(store.state.name)
}
}
也可以使用 mapState 来获取到
state:用来存放组件之间共享的数据。他跟组件的data选项类似,只不过data选项是用来存放组件的私有数据。getters:有时候,我们需要对state的数据进行筛选,过滤。这些操作都是在组件的计算属性进行的。如果多个组件需要用到筛选后的数据,那我们就必须到处重复写该计算属性函数;或者将其提取到一个公共的工具函数中,并将公共函数多处导入 - 两者都不太理想。如果把数据筛选完在传到计算属性里就不用那么麻烦了,getters就是干这个的,你可以把getters看成是store的计算属性。getters下的函数接收接收state作为第一个参数。那么,组件是如何获取经过getters过滤的数据呢? 过滤的数据会存放到$store.getters对象中。具体看一个例子:(再此没有初始化vue项目,请多多包涵)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<script src="./js/vuex.js"></script>
<script src="./js/vue2.0.js"></script>
<body>
<div id="app">
<hello></hello>
</div>
</body>
<script>
Vue.use(Vuex);
var myStore = new Vuex.Store({
state:{
//存放组件之间共享的数据
name:"jjk",
age:18,
num:1
},
mutations:{
//显式的更改state里的数据
change:function(state,a){
// state.num++;
console.log(state.num += a);
}
},
getters:{
getAge:function(state){
return state.age;
}
},
actions:{
//
}
});
Vue.component('hello',{
template:"<p @click='changeNum'>姓名:{{name}} 年龄:{{age}} 次数:{{num}}</p>",
computed: {
name:function(){
return this.$store.state.name
},
age:function(){
return this.$store.getters.getAge
},
num:function(){
return this.$store.state.num
}
},
mounted:function(){
console.log(this)
},
methods: {
changeNum: function(){
//在组件里提交
// this.num++;
this.$store.commit('change',10)
}
},
data:function(){
return {
// num:5
}
}
})
new Vue({
el:"#app",
data:{
name:"dk"
},
store:myStore,
mounted:function(){
console.log(this)
}
})
</script>
</html>
当点击p标签前,chrome中显示:
点击p标签后:
可以看出:更改state的数据并显示在组件中,有几个步骤:
1. 在mutations选项里,注册函数 函数里面装逻辑代码。
2.在组件里,this.$store.commit('change',payload) 注意:提交的函数名要一一对应
3.触发函数,state就会相应更改
4.在组件的计算属性里 this.$store.state 获取你想要得到的数据
actions:既然mutations只能处理同步函数,我大js全靠‘异步回调’吃饭,怎么能没有异步,于是actions出现了...
actions和mutations的区别:
1.mutations为同步调用(可以改变state)
2.Action 为异步调用(不可以直接改变state)(一般会在这个处理玩逻辑 调用 mutations)
再来看一下实例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<script src="./js/vuex.js"></script>
<script src="./js/vue2.0.js"></script>
<body>
<div id="app">
<hello></hello>
</div>
</body>
<script>
Vue.use(Vuex);
var myStore = new Vuex.Store({
state:{
//存放组件之间共享的数据
name:"jjk",
age:18,
num:1
},
mutations:{
//显式的更改state里的数据
change:function(state,a){
// state.num++;
console.log(state.num += a);
},
changeAsync:function(state,a){
console.log(state.num +=a);
}
},
getters:{
getAge:function(state){
return state.age;
}
},
actions:{
//设置延时
add:function(context,value){
setTimeout(function(){
//提交事件
context.commit('changeAsync',value);
},1000)
}
}
});
Vue.component('hello',{
template:`
<div>
<p @click='changeNum'>姓名:{{name}} 年龄:{{age}} 次数:{{num}}</p>
<button @click='changeNumAnsyc'>change</button>
</div>`,
computed: {
name:function(){
return this.$store.state.name
},
age:function(){
return this.$store.getters.getAge
},
num:function(){
return this.$store.state.num
}
},
mounted:function(){
console.log(this)
},
methods: {
changeNum: function(){
//在组件里提交
// this.num++;
this.$store.commit('change',10)
},
//在组件里派发事件 当点击按钮时,changeNumAnsyc触发-->actions里的add函数被触发-->mutations里的changeAsync函数触发
changeNumAnsyc:function(){
this.$store.dispatch('add', 5);
}
},
data:function(){
return {
// num:5
}
}
})
new Vue({
el:"#app",
data:{
name:"dk"
},
store:myStore,
mounted:function(){
console.log(this)
}
})
</script>
</html>
点击按钮一秒后,chrome中显示:
先整明白 context dispatch是什么东西:
context:context是与 store 实例具有相同方法和属性的对象。可以通过context.state
和context.getters
来获取 state 和 getters。
dispatch:翻译为‘派发、派遣’的意思,触发事件时,dispatch就会通知actions(跟commit一样一样的)参数跟commit也是一样的。
action的大体流程:
1.在actions选项里添加函数(异步)并提交到对应的函数(在mutation选项里)中 context.commit('changeAsync',value);
actions:{
add:function(context,value){
setTimeout(function(){
context.commit('changeAsync',value);
},1000)
}
}
2. 在组件里: changeNumAnsyc:function(){this.$store.dispatch('add', 5);} 将dispatch“指向”actions选项里的函数
3. 在mutations选项里,要有对应的函数 changeAsync:function(state,a){console.log(state.num +=a);}
总结:各个类型的 API各司其职,mutation 只管存,你给我(dispatch)我就存;action只管中间处理,处理完我就给你,你怎么存我不管;Getter 我只管取,我不改的。 action放在了 methods 里面,说明我们应该把它当成函数来用(讲道理,钩子函数也应该可以的) mutation是写在store里面的,这说明,它就是个半成品,中间量,我们不应该在外面去操作它。getter写在了 computed 里面,这说明虽然 getter我们写的是函数,但是我们应该把它当成计算属性来用。
2丶子父组建传值
1丶父组件向子组件传值
父组件:我们给子组件 child 传递一个 message 属性
子组件:我们在子组件 用 props 来接收 message
事件传递
1.在子组件中创建一个按钮,给按钮绑定一个点击事件
2.在响应该点击事件的函数中使用$emit来触发一个自定义事件,并传递一个参数
3.在父组件中的子标签中监听该自定义事件并添加一个响应该事件的处理方法
子组件给父组件传值 可以利用以上方法 的回调函数 传参完成
3丶依赖注入
vue中的依赖注入 provide 和 inject
provide
选项允许我们指定我们想要提供给后代组件的数据/方法。在这个例子中,就是 <google-map>
内部的 getMap
方法:
<template>
<div>
<Footer></Footer>
</div>
</template>
<script>
import Footer from '@/components/footer/index'
export default {
name: "Home",
provide: function () {
return {
foo: 'bar',
Test(){
console.log('我是测试的')
}
}
},
components:{
Footer
}
}
</script>
<style scoped>
</style>
<template>
<tabbar>
<tabbar-item>
<img slot="icon" src="../../assets/img/icon_nav_button.png">
<span slot="label">Wechat</span>
</tabbar-item>
<tabbar-item show-dot>
<img slot="icon" src="../../assets/img/icon_nav_msg.png">
<span slot="label">Message</span>
</tabbar-item>
<tabbar-item selected link="/component/demo">
<img slot="icon" src="../../assets/img/icon_nav_article.png">
<span slot="label">Explore</span>
</tabbar-item>
<tabbar-item badge="2">
<img slot="icon" src="../../assets/img/icon_nav_cell.png">
<span slot="label">News</span>
</tabbar-item>
</tabbar>
</template>
<script>
export default {
name: "footer",
inject: ['foo','Test'],
created(){
console.log(this.foo)
}
}
</script>
<style scoped>
</style>
依赖注入的优点 是 父组件provide 提供的 信息
所有子组件都可以注入 inject 使用
同样 也可以注入方法
4丶router
书写router.js
var routes = [
{
path:"/one",
component:{template:"#a"}
},
{
path:"/two",
component:{template:"#b"}
},
];
// 定义路由组件
var router = new VueRouter({
routes
});
// 定义路由
new Vue({
el:"#box",
router
});
// 创建和挂载实例
将模版增添链接
<div id="box">
<router-link to="/one">One</router-link>
<router-link to="/two">Two</router-link>
<router-view></router-view>
</div>
< router-link > 默认会被渲染成一个 <a>
标签 >>>to=""为我们定义的路由
< router-view > 路由匹配到的组件将渲染在这里
动态路由匹配
我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用『动态路径参数』(dynamic segment)来达到这个效果:
{
path:"/two:id",
component:{template:"#b"},
},
当我们在地址后面直接添加任意字符,我们会发现文档内容随着我们的更改而改变.
嵌套路由
我们经常将动态路由和嵌套路由共同使用,嵌套路由即是在原路由的基础上增加一个 children ,children 是一个数组.并且我们还需要在原来的组件上添加< router-view >来渲染 chlidren 里面的路由.
<template id="b">
<div>
第二个router
<router-view>
</router-view>
</div>
</template>
<template id="c">
<div>
user:{{ $route.params.id }}
</div>
</template>
{
path:"/two",
component:{template:"#b"},
children:[
{
path:":id",
component:{
template:"#c"
}
}
]
},
编程式导航
除了使用 <router-link> 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。
router.push(location)
想要导航到不同的 URL,则使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。
当你点击 <router-link> 时,这个方法会在内部调用,所以说,点击 <router-link :to="..."> 等同于调用 router.push(...)。
该方法的参数可以是一个字符串路径,或者一个描述地址的对象。例如:
// 字符串
router.push('home')
// 对象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: 123 }})
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
router.replace(location)
跟 router.push 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。
router.go(n)
这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n)。
// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)
// 后退一步记录,等同于 history.back()
router.go(-1)
// 前进 3 步记录
router.go(3)
// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)
命名路由
有时我们通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候。你可以在创建 Router 实例的时候,在 routes 配置中给某个路由设置名称。
我们直接在路由下添加一个 name 即可.
var routes = [
{
path:"/one",
name:"one",
component:{template:"#a"}
},
{
path:"/two",
name:"two",
component:{template:"#b"},
},
]
要链接到一个命名路由,可以给 router-link 的 to 属性传一个对象:
<router-link :to="{ name: 'one'}">User</router-link>
<router-link :to="{ name: 'two'}">User</router-link>
命名视图
有时候想同时(同级)展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar(侧导航) 和 main(主内容) 两个视图,这个时候命名视图就派上用场了。你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default。
<router-view></router-view>
<router-view></router-view>
当我们的视图如上时,我们会发现每一个路由被渲染了两次,所以我们需要为视图命名
var Foo = { template: '<div>foo</div>' }
var Bar = { template: '<div>bar</div>' }
var routes = [
{
path:"/one",
name:"one",
components:{
a:Foo,
b:Bar
}
},
]
重定向和别名
重定向
重定向(Redirect)就是通过各种方法将各种网络请求重新定个方向转到其它位置,用于网站调整或网页被移到一个新地址,它也是通过 routes 配置来完成,下面例子是从 /a 重定向到 /b:
var router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
别名
/a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a 一样。简单的说就是给 /a 起了一个外号叫做 /b ,但是本质上还是 /a
上面对应的路由配置为:
var router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})
『别名』的功能让你可以自由地将 UI 结构映射到任意的 URL,而不是受限于配置的嵌套路由结构。
5丶自定义指令
例:官网的例子
全局注册
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
注册局部指令
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
然后你可以在模板中任何元素上使用新的 v-focus
属性,如下:
<input v-focus>
钩子函数
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
-
bind
:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。 -
inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。 -
update
:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。 -
componentUpdated
:指令所在组件的 VNode 及其子 VNode 全部更新后调用。 -
unbind
:只调用一次,指令与元素解绑时调用。
接下来我们来看一下钩子函数的参数 (即 el
、binding
、vnode
和 oldVnode
)。
6丶过滤器
例:
<!-- 在双花括号中 -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
你可以在一个组件的选项中定义本地的过滤器:
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
或者在创建 Vue 实例之前全局定义过滤器:
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
new Vue({
// ...
})
下面这个例子用到了 capitalize
过滤器:
过滤器可以串联:
{{ message | filterA | filterB }}
在这个例子中,filterA
被定义为接收单个参数的过滤器函数,表达式 message
的值将作为参数传入到函数中。然后继续调用同样被定义为接收单个参数的过滤器函数 filterB
,将 filterA
的结果传递到 filterB
中。
过滤器是 JavaScript 函数,因此可以接收参数:
{ message | filterA('arg1', arg2) }}
在这个例子中,filterA
被定义为接收单个参数的过滤器函数,表达式 message
的值将作为参数传入到函数中。然后继续调用同样被定义为接收单个参数的过滤器函数 filterB
,将 filterA
的结果传递到 filterB
中。
过滤器是 JavaScript 函数,因此可以接收参数:
{{ message | filterA('arg1', arg2) }}
这里,filterA
被定义为接收三个参数的过滤器函数。其中 message
的值作为第一个参数,普通字符串 'arg1'
作为第二个参数,表达式 arg2
的值作为第三个参数
7 axios
使用
npm install axios --save-dev
import axios from 'axios'
Vue.use(axios) // 注意 这样的用法是有问题的,axios不支持Vue.use()的声明方式
axios拦截器的使用
当我们访问某个地址页面时,有时会要求我们重新登录后再访问该页面,也就是身份认证失效了,如token丢失了,或者是token依然存在本地,但是却失效了,所以单单判断本地有没有token值不能解决问题。此时请求时服务器返回的是401错误,授权出错,也就是没有权利访问该页面。
我们可以在发送所有请求之前和操作服务器响应数据之前对这种情况过滤。
// http request 请求拦截器,有token值则配置上token值
axios.interceptors.request.use(
config => {
if (token) { // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
config.headers.Authorization = token;
}
return config;
},
err => {
return Promise.reject(err);
});
// http response 服务器响应拦截器,这里拦截401错误,并重新跳入登页重新获取token
axios.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response) {
switch (error.response.status) {
case 401:
// 这里写清除token的代码
router.replace({
path: 'login',
query: {redirect: router.currentRoute.fullPath}//登录成功后跳入浏览的当前页面
})
}
}
return Promise.reject(error.response.data)
});
有一天有人问我 axios怎么解决跨域 我反问自己 前端可已解决跨域了? ,我只知道 配置webpack node拦截 发起请求
这样后端因为没有了请求源 就卡不住了
修改
config/index.js
dev: {
加入以下
proxyTable: {
'/api': {
target: 'http://40.00.100.100:3002/',//设置你调用的接口域名和端口号 别忘了加http
changeOrigin: true,
pathRewrite: {
'^/api': '/'//这里理解成用‘/api’代替target里面的地址,后面组件中我们掉接口时直接用api代替 比如我要调用'http://40.00.100.100:3002/user/add',直接写‘/api/user/add’即可
}
}
}
基本上 vue核心技术知识点 剩下的基本上就是杂草了 可以去扒扒vue官网