Vue学习

邂逅Vue.js

1.编程范式
- 命令式编程 如jquery
- 声明式编程 只需要生命需要显示的东西就可以
2. 创建Vue实例的时候,传入了一个对象options,这个opentions中包含了
- el: 类型:string|HTMLElement 作用:决定之后Vue实例会管理哪一个DOM
- data: 类型:Object|Function(组件中data必须是一个函数) 作用:Vue实例对应的数据对象
- methods: 类型:{[key: string] : Function} 作用: 定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用。

Vue基础语法

  1. 前端代码缩进最好两个空格,多数大型开源框架都是两个

模板语法

Mustache语法({{}})

  1. 可以写变量,也可以写简单的表达式
    {{message}}
    {{firstName + ' ' + lastName}}
    

指令

  1. v-once
    - 该指令后面不需要跟任何表达式
    - 该指令表示元素和组件只渲染一次,不会随数据的改变而改变
    <h2 v-once>{{message}}<h2>
    
  2. v-html
    - 该指令后面往往会跟上一个string类型
    - 会将string的html解析出来并且进行渲染
    <h2 v-html='url'><h2>
    data:{
    	url: '<a href="http://www.baidu.com">百度一下<a>'
    }
    
  3. v-text
    {{message}}
    <h2 v-text='message'><h2>
    
    一般不用,不够灵活,会覆盖innerHTML内容
  4. v-pre , 原生显示
    <h2 v-pre>{{message}}</h2>
    
  5. 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>
    
  6. 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}
     }
    }
    
  7. v-bind:style
    - 动态绑定样式
    - <h2 :style="{fontSize: '50px'}"></h2>
    - <h2 style="{fontSize: finalSize + 'px'}"></h2>

计算属性

  1. 数据展示前需要进行处理时使用。
    computed:{
    	fullName: function(){
    		return this.firstName + this.lastName
    		}
    }
    
  2. 计算属性会缓存,多次调用只会计算一次,方法不会缓存
  3. 计算属性的getter和setter(set不常用)
    computed: {
    	fullName: {
    		get: function(){
    			return this.firstName + ' ' + this.lastName
    		},
    		setfunction(name){
    			this.fullName = name
    		}
    	}
    }
    

对象字面量增强写法

 // 普通写法
 name = 'aaa'
 age = 18
const obj = {
	name: name,
	age: age
}
// 增强写法
const obj = {
	name,
	age
}

事件监听

  1. v-on
    - 作用: 绑定事件监听器
    - 缩写: @
    - 预期: Function| Inline Statement | Object
    - 参数: event
  2. 参数传递
    - 如果方法不需要额外参数,那么方法后的()可以不添加
    - 如果方法本身有参数,但是调用的时候没有传递,省略小括号,默认会将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)
    	}
    }
    
  3. v-on修饰符的使用
    - .stop 阻止冒泡
      <div @click='divClick'>
        <button @click.stop="btnClick"></button>
     </div>
    
        - prevent   阻止默认事件
    
    <form action="baidu" @click.prevent="submitClick">
    </form>
    # 阻止默认提交事件
    
      - .{keyCode | keyAlias}   只当事件是从特定键触发时才触发回调
      - .native   监听根元素的原生事件
      - .once  只触发一次回调
    

条件判断

  1. v-if
  2. v-else-if
  3. v-else
  4. vue内部会复用dom元素,如果不想让复用,可以加个key
  5. v-show
    - v-show dom会渲染,使用样式决定是否显示
    - v-if 根据条件判断是否要渲染
    - 频繁切换使用v-show

循环

  1. v-for遍历
    官方推荐在使用v-for遍历时,给对应的元素或组件加上一个:key属性
    为什么要使用key属性?
    假设有一个数组,现在想在数组中间插入一个元素,如果没有key,则插入位置以及后面的位置都需要改变,而如果有key,则会先使用key与元素对应起来,插入时只修改插入位置即可。key必须与item一一对应,所以不能用index。
    遍历时key的作用
  • 使用key给每一个节点做一个唯一标识
  • Diff算法可以正确的识别此节点
  • 找到正确的位置插入新的节点
    key的作用主要是为了更高效的更新虚拟DOM
  1. 哪些数组的方法是响应式的?
    - 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>
  1. 高阶函数
    - filter() : filter回调函数必须返回一个bool值
    - map()
    - reduce(): 对数组中所有内容进行汇总
    arr = [1,2,3,4]
    arr.reduce(function(preValue, n){
    	return preValue + n;
    }, 0)
    0 表示preValue的初始值, n表示每次遍历时的元素
    
  2. 表单绑定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;
    	}
    }
    
  3. 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: '男'
      }
    
  4. 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: []
    }
    
  5. v-select :单选(字符串) 多选(数组)
  6. 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">

组件化开发

组件注册步骤

  1. 创建组件构造器对象
  2. 注册组件
  3. 使用组件
    const cpn = Vue.extend({
    	template: `<div><h2>hhhhh</h2></div>`
    })
    Vue.components('my-cpn', cpn)
    
  4. 全局组件和局部组件
    - 全局组件可以在多个Vue实例中使用
    - 局部组件
    const app = new Vue({
    	el: '#app',
    	data:{}
    	conponents: {
    		cpn: cpn     #局部组件
    	}
    }
    
  5. 父组件和子组件
  6. 注册组件语法糖, 主要时省去了调用Vue.extend()的步骤,而是直接可以使用一个对象来代替
    // 全局
    Vue.components('cpn', {
    	template: `<div><h2>hhhhh</h2></div>`
    })
    

组件间的通信

父子组件的通信

  1. 通过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>

父子组件的访问方式

  1. 父组件访问子组件:使用$children或$refs
    this.$children 是一个数组,包含了所有的子组件
    <child  refs='aaa'></child>
    在父组件中使用this.$refs.aaa获取对应子组件
    
  2. 子组件访问父组件:使用$parent
  3. 访问根组件$root

插槽(slot)

  • 组件的插槽是为了让封装的组件更加有扩展性
  • 让使用者可以决定组件内部的一些内容到底展示什么
  1. 插槽的基本使用
    <slot></slot>
  2. 插槽的默认值
    <slot><button>按钮</button></slot>
  3. 如果有多个值,同时放到组件中替换,一起作为替换元素

具名插槽

<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入门

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的区别

image-20201128195831819.png

//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 build过程

image-20201128211644528.png

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修改配置的方法

  1. vue ui启动一个本地服务,在界面上修改

  2. node_modules下面的@vue/webpack.config.js

  3. 在当前项目下创建一个文件,vue.config.js

    module.exports = {
    }
    

vue-router

Vue-router认识路由前端渲染与后端渲染url的hash和HTML5的historyvue-router的基本使用路由的懒加载vue-router嵌套路由Vue-router参数传递Vue-router导航守卫keep-alive

Vue-router

认识路由

  1. 概念

    路由(routing)就是通过互联的网络把信息从源地址传输到目的地址的活动

  2. 路由器提供了两种机制:路由和转送

    1. 路由是决定数据包从来源到目的地的路径

    2. 转送将输入端的数据转移到合适的输出端

  3. 路由表

  4. 路由表本质上就是一个映射表,决定了数据包的指向

前端渲染与后端渲染

  1. 区别

  2. 后端路由

    1. 后端处理URL与页面间的映射关系
  3. 前后端分离

    1. 后端只负责提供数据,不负责任何界面的内容

    2. 前端渲染:浏览器中显示的网页中的大部分内容,都是由前端写的js代码在浏览器中执行,最终渲染出来的网页

    3. 后端只提供 API来返回数据,前端通过Ajax返回数据,通过js将数据渲染到页面中

  4. 单页面富应用(SPA页面)

    1. 在前后端分离基础上,又加上了一层前端路由。区别

    2. 整个网页只有一个html页面

    3. 改变url,但是页面不进行整体的刷新

url的hash和HTML5的history

  1. url的hash

    1. url的hash也就是锚点(#),本质上是改变window.location的href属性

    2. 可以通过直接赋值location.hash来改变href,但是页面不发生刷新

    3.  location.hash = "aaa"
       url变为"localhost:8080/aaa"</pre>
      
  2. HTML5的history模式

    1. pushState
       history.pushState({},'',home)   // localhost:8080/home
       history.pushState({},'',about)   // localhost:8080/about
       history.pushState({},'',mine)   // localhost:8080/mine
       //以上相当于入栈
       history.back() //后退,相当于出栈
       history.forward() //前进,相当于压栈</pre>
      
    2. replaceState(): 替换url,back方法失效

vue-router的基本使用

  1. 安装
   npm install vue-router --save
  1. 使用

    1. 导入路由对象,并且调用Vue.use(VueRouter)

    2. 创建路由实例,并传入路由映射配置

    3. 在Vue实例中挂载创建的路由实例

  2. 配置路由映射关系

    1. 创建路由组件

    2. 配置url和组件的映射关系

    3. 使用路由,通过 和

      <router-link to="/home"></router-link>
       <router-link to="/about"></router-link>
       <router-view></router-view></pre>
      
  3. 路由的默认路径

     path: '',
     redirect: '/home'
    }]
    
  4. 默认是hash模式,可以修改成history模式

      routes;
      mode: 'history'
     })
    
  5. router-link的属性

    1. to:由于指定跳转的路径

    2. tag:默认router-link会渲染成a标签,如果想渲染成别的标签,则可以使用此属性

    3. replace:replace不会留下history记录,前进后退无法使用,相当于replaceState()

    4. active-class: 激活的router-link默认会有一个router-link-active的类,如果不想使用默认类,则可以使用这个属性

    5.  //如果修改active-class,如果有多个router-link可能需要多处修改,也可以修改一个地方
       const router = new VueRouter({
        routes;
        mode: 'history',
        linkActiveClass: 'active'
       })
      
  6. 通过代码修改路由 this.$router.push('/home') 可以使用返回前进按钮 this.$router.replace('/home') 前进后退不能用

  7. 动态路由

    export default {
     name: '#app',
     data(){
     return {
     userId: 'zhangsan'
     }
     }
    }
    {
     path: '/user/:userId',
     component: User
    }
    //获取参数
    this.$route.params.userId
    {{$route.params.userId}}
    

路由的懒加载

  1. npm run build后生成3个js文件 app.js manifest.js vender.js

    app.js中是自己写的业务代码

    manifest.js是为了打包的代码做底层支撑

    vender.js中的是使用的第三方包

  2. 为什么要懒加载?

    1. 打包构建应用时,js文件比较大,会影响页面加载

    2. 如果能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样更加高效

  3. 如何进行路由懒加载

    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嵌套路由

  1. 什么是嵌套路由?

    1. 比如在Home页面,希望通过/home/news和/home/message访问一些内容

    2. 一个路径映射一个组件,访问这两个路径也会分别渲染两个组件

  2. 如何实现嵌套路由?

    1. 创建对应的子组件,并在路由映射中配置对应的子路由

    2. 在组件内部使用标签

      {
      path: '/home',
      component: Home,
      children: [
      path: 'news',  //前面不要加/
      components: News
      ]
      }
     ]
    

Vue-router参数传递

  1. 传递参数主要有两种类型params和query

    1. params的类型

      1. 配置路由格式:/router/:id

      2. 传递的方式:在path后面跟上对应的值

      3. 传递后形成的路径:/router/123, /router/abc
        <router-link :to="'/profile' + userId"></router-link>

    2. query的类型

      1. 配置路由格式:/router

      2. 传递的方式:对象中使用query的key作为传递方式

      3. 传递后形成的路径:/router?id=123, /router?id=abc

             <h2>{{$route.query.name}}</h2>  //取参
    

Vue-router导航守卫

  1. 什么是导航守卫?

    1. vue-router提供的导航守卫主要用来监听路由的进入和离开

    2. vue-router提供了beforeEach和afterEach的钩子函数,他们会在路由改变前和路由改变后触发

  2. 一个小需求,根据路由的变化修改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)=>{})
  1. 除了全局守卫,还有路由守卫和组件内守卫

    1.   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详解

  1. Vuex是一个为Vue.js应用程序开发的状态管理模式

    1. 采用集中存储管理应用的所有组件的状态
  2. 管理什么状态?

    1. 多个页面间共享的,如用户登录状态,用户名称,头像,地理位置信息,商品的收藏,购物车中的商品
  3. //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')
      }
    }
    
  4. 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

  1. Vue发送网络请求有非常多的方式,如何选择?

    1. 传统的Ajsx是基于XMLHttpRequest(XHR)
      1. 配置和调用方式非常混乱
    2. jQuery-Ajax
      1. 为了一个网络请求,引用jQuery,得不偿失
    3. Vue1.x,有个Vue-resource
      1. Vue2.0已经去掉,不会再更新
  2. 为什么axios

    1. 在浏览器中发送XMLHttpRequests请求
    2. 在node.js中发送http请求
    3. 支持Promise API
    4. 拦截请求和响应
    5. 转换请求和响应数据
  3. axios请求方式

    1. axios(config)

      1. axios({
          url: 'http://123.207.32.32:8000/home/multidata',
          method: 'get', //默认get请求
        }).then((res)=>{
          console.log(res)
        })
        
      2. 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)
        })
        
    2. axios.request(config)

    3. axios.get(url[, config])

    4. axios.delete(url[,config])

    5. axios.head(url[,config])

    6. axios.post(url[, data [,config]])

    7. axios.put(url[, data[, config]])

    8. axios.patch(url[, data[,config]])

  4. axios发送并发请求

    1. axios.all([axios(), axios()]).then(results => {})
      //可以将结果展开
      axios.all([axios(), axios()]).then(axios.spread((res1, res2) => {}))
      
  5. 全局配置

    1. //公共的配置
      axios.defaults.baseUrl = "http://localhost:8080"
      axios.defaults.timeout = 5000   //超时时间5秒
      
    2. 常见的配置

      1. 请求地址: url: '/user'
      2. 请求类型: method: 'get'
      3. 根路径: baseUrl: 'http://localhost:8080'
      4. 请求前的数据处理: transformRequest: [function(data){}]
      5. 请求后的数据处理: transformResponse: [function(data){}]
      6. 自定义请求头: headers: {'x-Requested-With': 'XMLHttpRequest'}
      7. URL查询对象: params: {id: 12}
      8. 查询对象序列化函数: paramsSerializer: fucntion(params){}
      9. request body: data:{key: 'aa'}
      10. 超时设置: timeout: 10000
      11. 跨域是否带token: withCredentials: false
      12. 自定义请求处理: adapter: function(resolve, reject, config){}
      13. 身份验证信息:auth: {uname: '', pwd: '1243'}
      14. 响应的数据格式: responseType: 'json/blob/document/arraybuffer/text/stream'
  6. axios实例

    1. import axios from 'axios'
      const instance = axios.create({
        bseURL: 'http://localhost:8080',
        timeout: 5000
      })
      //使用
      instance({
        url: '/user'
      }).then((res)=>{})
      
  7. 拦截器

    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就可以
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值