• 一篇文章解锁vue2


    本文章对标vue2笔记内容,欢迎补充

    Vue介绍

    Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用,Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动

    Vue2的生命周期

    在这里插入图片描述

    生命周期钩子

    • beforeCreate: 初始化之前,用于添加一些一开始就要加载的效果,如window.load()

    • created:初始化之后,用于发送一些网络请求来获取数据

    • beforeMount:页面挂载之前,此时的Dom操作已无效

    • mounted:页面挂载之后,用于启动定时器等

    • beforeUpdate:页面更新之前,用于移除事件监听器

    • updated:页面更新之后

    • beforeDestroy:vue实例被销毁前,用于清除定时器以及其他监听事件等,此时操作页面已不会更新

    • destroyed:vue实现销毁后

    • activated:被keep-alive缓存的组件激活时

    • deactivated:被keep-alive缓存的组件停用时

    使用vue/cli(脚手架)创建项目工程

    使用vue create xxx来创建项目,选择vue版本,可以手动选择需要的配置

    在这里插入图片描述

    然后npm run serve启动项目

    在这里插入图片描述

    可以看到项目已经创建完毕,运行版vue.js没有模板解析器,需要使用render函数来获取组件

    在这里插入图片描述

    render原理相当于

    render:function(createElement){
    	return createElement(组件){}
    }
    

    组件

    vue使用组件化开发来代替多页面的切换,以进行单页面开发,即这些.vue文件

    .vue文件有三块基本区域:

    template、script、style分别是显示区域、处理事件区域、样式区域,如:

    在这里插入图片描述

    最后需要使用export default将组件暴露出去,供上级使用如:

    <script>
    // @ is an alias to /src
    import HelloWorld from '@/components/HelloWorld.vue'
    
    export default {
      name: 'HomeView',   // 组件名
      components: {
        HelloWorld        // 子组件
      }
    }
    </script>
    

    es6三种暴露方法

    // 分别(多行)暴露
    export function(){}		// 暴露
    import {} from '路径'		// 导入
    // 统一暴露
    function a ; function b ; export {a,b}		// 暴露
    import {a,b} from '路径'					// 导入
    // 默认暴露
    export default function a(){}		// 暴露
    import a from '路径'				// 导入
    

    属性

    ref

    用于获取元素,最早可在mounted钩子使用,如:

    <template>
      <div class="home">
        <img alt="Vue logo" src="../assets/logo.png" ref="imgs" @click="innimg()">
        <HelloWorld msg="Welcome to Your Vue.js App"/>
      </div>
    </template>
    
    <script>
    // @ is an alias to /src
    import HelloWorld from '@/components/HelloWorld.vue'
    
    export default {
      name: 'HomeView',   // 组件名
      components: {
        HelloWorld        // 子组件
      },
      methods:{
        innimg(){
          this.$refs.imgs.classList.add("bic")
        }
      },
    }
    

    已成功添加class

    在这里插入图片描述

    props

    用于接收父组件的传参,如:

    <template>
      <div class="home">
        <img alt="Vue logo" src="../assets/logo.png" ref="imgs"">
    	<!-- 为子组件传输msg -->
        <HelloWorld msg="Welcome to Your Vue.js App"/>
      </div>
    </template>
    
    <script>
    // @ is an alias to /src
    import HelloWorld from '@/components/HelloWorld.vue'
    
    export default {
      name: 'HomeView',   // 组件名
      components: {
        HelloWorld        // 子组件
      }
    }
    
    <template>
      <div class="hello">
        <!-- 显示msg -->
        <h1>{{ msg }}</h1>
      </div>
    </template>
    
    <!-- 使用props接收父组件的msg -->
    <script>
    export default {
      name: 'HelloWorld',
      props: {
        msg: String
      }
    }
    </script>
    

    若有同名数据冲突:

    优先级: props>Methods>Data>Computed>Watch

    mixin

    将多个组件都需要使用的函数、data等混入,如:

    // mixin.js
    export default {
      data: function() {
        return {
          mixinData: '来自 Mixin 的数据'
        };
      },
      methods: {
        mixinMethod: function() {
          console.log('这是 Mixin 中的方法');
        }
      }
    };
    
    // 使用混入的数据
    <template>
      <div class="about">
        <h1>This is an about page</h1>
        <h2>{{ mixinData }}</h2>
      </div>
    </template>
    
    <script>
    import mixin from '@/mixin/mixin';
    
    export default{
      name: 'AboutView',
      mixins: [mixin]
    }
    </script>
    

    plugins插件

    包含install方法的一个对象,也可以直接是一个安装函数本身。安装函数会接收到安装它的应用实例和传递给 app.use() 的额外选项作为参数

    // 安装一个插件
    import { createApp } from 'vue'
    const app = createApp({})
    app.use(myPlugin, {
      /* 可选的选项 */
    })
    // 定义插件
    const myPlugin = {
      install(app, options) {
        // 配置此应用
      }
    }
    

    数组更新检测(会改变原数组)

    • push() 添加数组元素

    • pop() 删除数组最后一个元素

    • shift() 删除数组第一个元素

    • unshift() 在数组开头添加一个元素

    • splice() array.splice(位置,个数,若有新元素则此函数为增加否则为删除),如果删除一个元素,则返回一个元素的数组。 如果未删除任何元素,则返回空数组

    • sort() 数组排序,默认按字母升序,原理:

    var points = [40,100,1,5,25,10];
    points.sort(function(a,b){return a-b});	// 升序
    points.sort(function(a,b){return b-a});	// 降序
    
    • reverse() 反转数组顺序

    添加/修改响应式布局

    对于已经创建的实例,Vue 不允许动态添加根级别的响应式 property。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式 property。例如,对于:

    Vue.set(vm.someObject, 'b', 2)
    

    您还可以使用 vm.$set 实例方法,这也是全局 Vue.set 方法的别名:

    this.$set(this.someObject,'b',2)
    

    需要为已有对象赋值多个新 property时:

    // 代替Object.assign(this.someObject, { a: 1, b: 2 })
    this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
    

    vue内置指令

    • v-on (@):用于添加事件。事件修饰符:

    ​ stop 阻止事件继续传播冒泡

    ​ prevent 阻止默认事件

    ​ once 事件只触发一次

    ​ capture 使用捕获模式

    ​ self 只有event.target是当前操作的元素才能触发事件

    ​ passive 事件默认行为立即执行

    注意:在捕获模式中,事件会从外层元素开始向内层元素进行传播,直到达到目标元素。而在冒泡模式中,事件则是从目标元素开始,由内向外进行传播。在Vue.js中,默认情况下事件监听器采用冒泡模式。开发者可以使用.capture修饰符来设置事件监听器使用捕获模式。

    • v-model :双向绑定。修饰符:

    ​ trim 去掉前后空格

    ​ number 限制内容为数值类型

    ​ lazy 失去焦点时再收集内容

    • v-bind : 绑定数据和元素属性(如要添加的类名、a标签的href属性等)

    • v-once: 一次性的插值,数据发送改变时,此处内容不会改变

    • v-html: 将html标签解析(容易被css攻击,不要对用户所输入的内容使用)

    • v-cloak: 当vue开始接管容器时会自带清除这个属性,配合属性选择器来使用

    • v-pre: 让vue不去解析这条语句

    • v-show: 显示与隐藏

    自定义属性directives

    除了 Vue 内置的一系列指令 (比如 v-modelv-show) 之外,Vue 还允许你注册自定义的指令 (Custom Directives)

    一个自定义指令由一个包含类似组件生命周期钩子的对象来定义。钩子函数会接收到指令所绑定元素作为其参数。下面是一个自定义指令的例子,当一个 input 元素被 Vue 插入到 DOM 中后,它会被自动聚焦:

    <script setup>
    // 在模板中启用 v-focus
    const vFocus = {
      mounted: (el) => el.focus()
    }
    </script>
    
    <template>
      <input v-focus />
    </template>
    

    reduce方法

    reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。

    reduce() 可以作为一个高阶函数,用于函数的 compose

    在这里插入图片描述

    <button onclick="myFunction()">点我</button>
     
    <p>数组元素之和: <span id="demo"></span></p>
     
    <script>
    var numbers = [15.5, 2.3, 1.1, 4.7];
     
    function getSum(total, num) {
        return total + Math.round(num);
    }
    function myFunction(item) {
        document.getElementById("demo").innerHTML = numbers.reduce(getSum, 0);
    }
    </script>
    

    webstorage

    localstorage 本地存储

    sessionstorage 不是持久化的本地存储,是会话级别存储,当页面关闭后就会被销毁

    cookie 用于服务器交互,大小受到限制,每次请求一个新的页面时cookie都会被发送过去(浪费宽带),不能跨域使用,需要自己封装set,get

    方法

    • webstorage.setItem(‘name’,‘message’) 保存message到name

    • webstorage.getItem(‘name’) 查看name

    • webstorage.removeItem(‘name’) 删除name

    • webstorage.clear() 清空storage

    JSON

    js对象,用于存储和交换文本信息

    • JSON.parse() 将数据转换为js对象

    • JSON.stringify() 将js对象转换为字符串,服务器端数据一般为字符串

    Null

    值 null 被写作字面量:null。不像 undefined,null不是全局对象的属性。相反,null是表示缺少的标识,指示变量未指向任何对象。在 API 中,null常在预期的值应是一个对象,但又没有关联的对象的地方使用,undefined是全局对象的一个属性。也就是说,它是全局作用域的一个变量。undefined的最初值就是原始数据类型undefined

    自定义事件

    使用v-bind绑定自定义事件给子组件实现子传父,利用ref属性获取组件实例来传输自定义事件

    使用this. r e f . 自定义事件 . ref.自定义事件. ref.自定义事件.on(自定义方法)

    使用this.$emit()触发自定义事件

    使用this.$off() 解绑自定义事件,多个事件用数组形式

    示例(子组件传父组件):

    <template>
      <div>
        <hello-world @myEvent="getC"></hello-world>
        <div>{{ cData }}</div>
      </div>
    </template>
    
    <script>
    // 父组件
    import HelloWorld from '@/components/HelloWorld.vue';
    export default {
      components: { HelloWorld },
      name: 'HelloV',
      data(){
        return{
          cData:''
        }
      },
      methods: {
        getC(c){
          this.cData = c
        }
      }
    }
    </script>
    
    <template>
      <div class="hello">
          <button @click="sendFa()">给父组件</button>
      </div>
    </template>
    
    <script>
    // 子组件
    export default {
      name: 'HelloWorld',
      props: {
        msg: String
      },
      methods:{
        sendFa(){
          const c = 'cData'
          this.$emit('myEvent',c)
        }
      }
    }
    </script>
    

    pubsub:消息订阅与发布

    适用于组件间通信

    安装

    npm i pubsub-js

    导入

    import pubsub from ‘pubsub-js’

    使用

    订阅消息/接收数据,一般在mounted钩子中使用

    this.pubid = pubsub.subscribe(‘自定义方法’,回调函数)

    发布消息/提供数据

    pubsub.publish(‘自定义方法’,数据)

    示例(子组件传父组件):

    <template>
      <div class="hello">
        <h1>{{ msg }}</h1>
        <p>
          <button @click="sendFa()">给父组件</button>
        </p>
      </div>
    </template>
    
    <script>
    import PubSub from 'pubsub-js';
    export default {
      name: 'HelloWorld',
      props: {
        msg: String
      },
      methods:{
        sendFa(){
          // 发送消息 
          const c = 'cData'
          // this.$emit('myEvent',c)
          PubSub.publish('hello',c)
        }
      }
    }
    </script>
    
    
    <template>
      <div>
        <hello-world @myEvent="getC"></hello-world>
        <div>{{ cData }}</div>
      </div>
    </template>
    
    <script>
    import PubSub from 'pubsub-js';
    import HelloWorld from '@/components/HelloWorld.vue';
    export default {
      components: { HelloWorld },
      name: 'HelloV',
      data(){
        return{
          cData:''
        }
      },
      methods: {
        getC(){
        }
      },
      mounted(){
        // 接收消息 
        const _this = this
        PubSub.subscribe('hello',(msg,c)=>{
          _this.cData = c
          console.log(msg);
          console.log(c);
        })
      }
    }
    </script>
    
    

    全局事件总线

    GlobalEventBus:让所有组件可相互通信

    使用vc或vm作为中转,在beforeCreate钩子内创建,一般以$bus命名

    依赖于一个重要的内置关系:

    VueComponent.prototype.__ proto__ ===Vue.prototype

    让组件实例对象可以访问到vue原型上的属性方法

    使用

    首先挂载到main.js

    import Vue from 'vue'
    import App from './App.vue'
    import router from './router'
    import store from './store'
    
    Vue.config.productionTip = false
    
    
    new Vue({
      router,
      store,
      render: h => h(App),
      beforeCreate(){
        Vue.prototype.$bus = this
      }
    }).$mount('#app')
    
    

    父组件监听自定义事件

    <template>
      <div>
        <hello-world @myEvent="getC"></hello-world>
        <div>{{ cData }}</div>
      </div>
    </template>
    
    <script>
    import HelloWorld from '@/components/HelloWorld.vue';
    export default {
      components: { HelloWorld },
      name: 'HelloV',
      data(){
        return{
          cData:''
        }
      },
      methods: {
        getC(){
          
        }
      },
      mounted(){
        this.$bus.$on('myEvent',(data)=>{
          this.cData = data
        })
      },
      // 用完就解绑自定义事件
       beforeDestroy(){
        this.$bus.$off('myEvent')
      }
    }
    </script>
    
    

    子组件通过自定义事件发送数据

    <template>
      <div class="hello">
        <h1>{{ msg }}</h1>
        <button @click="sendFa()">给父组件</button>
      </div>
    </template>
    <!-- 使用props接收父组件的msg -->
    <script>
    export default {
      name: 'HelloWorld',
      props: {
        msg: String
      },
      methods:{
        sendFa(){
          const c = 'cData'
          this.$bus.$emit('myEvent',c)
        }
      }
    }
    </script>
    
    
    

    动画与过渡

    @keyframes属性

    from 开始时, to 结束时

    <template>
      <div>
        <div class="ani"></div>
      </div>
    </template>
    
    
    <style scoped>
    .ani{
      margin-left: 40%;
      background: black;
      width: 200px;
      height: 200px;
      animation: anime 5s infinite;
    }
    @keyframes anime{
      from{
        rotate: 0;
      }
      to{
        rotate: 180deg;
      }
    }
    </style>
    

    transition

    是一个内置组件,这意味着它在任意别的组件中都可以被使用,无需注册。它可以将进入和离开动画应用到通过默认插槽传递给它的元素或组件上。进入或离开可以由以下的条件之一触发:

    • 由 v-if 所触发的切换

    • 由 v-show 所触发的切换

    • 由特殊元素 切换的动态组件

    • 改变特殊的 key 属性

    例如:

    <template>
      <div>
        <hello-world @myEvent="getC"></hello-world>
        <div>{{ cData }}</div>
        <transition>
          <div class="ani" v-if="show"></div>
        </transition>
        <button @click="show = !show">000</button>
      </div>
    </template>
    
    <script>
    import HelloWorld from '@/components/HelloWorld.vue';
    export default {
      components: { HelloWorld },
      name: 'HelloV',
      data(){
        return{
          cData:'',
          show: false
        }
      },
     
    }
    </script>
    
    <style scoped>
    .ani{
      width: 200px;
      height: 200px;
      margin-left: 40%;
      background-color: black;
    }
    .v-enter-active,
    .v-leave-active {
      transition: opacity 0.5s ease;
    }
    
    .v-enter-from,
    .v-leave-to {
      opacity: 0;
    }
    </style>
    
    • .v-enter-active 整个进入的过程

    • .v-enter 进入时

    • .v-leave-active 整个离开的过程

    • .v-leave 离开时

    • .v-enter-to 进入的终点

    • .v-leave-to 离开的终点

    想要先执行离开动画,然后在其完成之后再执行元素的进入动画,可以加入mode如下:

    <Transition mode="out-in">
      ...
    </Transition>
    

    transition-group

    支持和 基本相同的 props、CSS 过渡 class 和 JavaScript 钩子监听器,但有以下几点区别:

    • 默认情况下,它不会渲染一个容器元素。但你可以通过传入 tag prop 来指定一个元素作为容器元素来渲染。
    • 过渡模式在这里不可用,因为我们不再是在互斥的元素之间进行切换。
    • 列表中的每个元素都必须有一个独一无二的 key attribute。
    • CSS 过渡 class 会被应用在列表内的元素上,而不是容器元素上。
    <template>
      <div>
        <hello-world @myEvent="getC"></hello-world>
        <div>{{ cData }}</div>
        <transition>
          <div class="ani" v-if="show"></div>
        </transition>
        <button @click="show = !show">000</button>
        <TransitionGroup name="list" tag="ul" style="margin-left: 30%;">
          <li v-for="item in items" :key="item">
            {{ item }}
          </li>
        </TransitionGroup>
        <button @click="items--">000</button>
      </div>
    </template>
    
    <script>
    import HelloWorld from '@/components/HelloWorld.vue';
    export default {
      components: { HelloWorld },
      name: 'HelloV',
      data(){
        return{
          cData:'',
          show: false,
          items: 6
        }
      },
      methods: {
        getC(){
          
        }
      },
    }
    </script>
    
    <style scoped>
    .ani{
      width: 200px;
      height: 200px;
      margin-left: 40%;
      background-color: black;
    }
    .list-enter-active,
    .list-leave-active {
      transition: all 0.5s ease;
    }
    .list-enter-from,
    .list-leave-to {
      opacity: 0;
      transform: translateX(30px);
    }
    </style>
    

    Promise

    js中进行异步编程的新解决方案

    promise是一个构造函数,promise对象用来封装一个异步操作并获取其成功/失败的结果值,异步方法不会立即返回最终值,而是返回一个 promise,以便在将来的某个时间点提供该值

    一个 Promise 必然处于以下几种状态之一:

    • 待定(pending):初始状态,既没有被兑现,也没有被拒绝。
    • 已兑现(fulfilled):意味着操作成功完成。
    • 已拒绝(rejected):意味着操作失败

    可以使用async来创建一个异步函数,示例如下:

    <script>
    export default {
      name: 'HelloV',
      data(){
        return{
          ob1: {
            io: 1,
            vc: '255'
          }
        }
      },
      methods: {
        // 创建异步方法
        async getOb(){
          return this.ob1
        },
        // 获取异步方法返回的promise对象,使用then方法获得promise中的对象,并使用解构赋值提取其中一个元素
        getOb1(){
          this.getOb().then((value)=>{
            const {io} = value
            console.log(io)
          })
        }
      }
    }
    </script>
    

    .then方法:用于调用resolve和reject方法,返回一个promise对象,例如:

    <script>
    export default {
      name: 'HelloV',
      methods: {
        getProm(){
          const pro = new Promise((resolve)=>{
            // resolve 调用视为成功,改变此Promise状态为已成功
            resolve('27')
          }).then((value)=>{
            // 使用.then方法获取resolve方法的值,并返回一个Promise对象
            console.log(value,pro);
          })
        }
      },
    }
    </script>
    

    在这里插入图片描述

    可以看到Promise状态变为fulfilled

    再来看看reject方法

    <script>
    export default {
      name: 'HelloV',
      methods: {
        getProm(){
          const pro = new Promise((resolve,reject)=>{
            reject('20')
          }).then((reason)=>{
            console.log(reason,pro);
          }).catch((e)=>{
            console.log(e);
          })
        }
      },
    }
    </script>
    

    以上内容解释:

    首先创建一个Promise对象,Promise构造函数有两个参数:resolverejectresolve用于将Promise的状态标记为已成功,并传递一个成功值;reject用于将Promise的状态标记为已失败,并传递一个失败原因,这里使用的是reject方法,所以会使Promise的状态变为已失败,并传递’20’作为失败原因,.catch函数用于捕获Promise失败的函数,如果此时不捕获错误则会报错,.catch函数的参数为失败原因,由于Promise失败了,.then函数不会执行,.catch捕获到错误原因控制台输出’20’

    在这里插入图片描述

    throw 抛出错误

    这里没有使用catch捕获,直接throw抛出错误

    <script>
    export default {
      name: 'HelloV',
      methods: {
        getProm(){
          const pro = new Promise((resolve,)=>{
            // resolve 调用视为成功,改变此Promise状态为已成功
            resolve('20')
          }).then((value)=>{
            throw value
          })
          console.log(pro)
        }
      }
    }
    </script>
    

    效果如下:

    在这里插入图片描述

    ajax

    向服务器发送异步请求,无需刷新获取数据

    XML

    可扩展标记语言,用于传输与存储数据,与html不同,没有预定义标签,全是自定义标签,现已经被JSON取代

    核心对象XMLHttpRequest

    使用步骤:

    <script>
    export default {
      name: 'HelloV',
      methods: {
        getXML(){
          // 创建核心对象
          const xhr = new XMLHttpRequest()
          // 设置请求信息
          xhr.open("GET","/js/test.JSON",true)
          // 发送请求
          xhr.send(0)
          // 进行判断输出
          xhr.onreadystatechange = ()=>{
            if(xhr.readyState !== 4)  return
            if(xhr.status >= 200 && xhr.status < 300){
              console.log(xhr.responseText);
            }
          }
        }
      }
    

    已经成功显示

    在这里插入图片描述

    http状态码(xhr.status)

    1xx: 信息正在处理

    2xx: 请求成功

    3xx: 附加操作(重定向)

    ​ 301: 永久重定向

    ​ 302: 临时重定向

    4xx: 无法处理(客户端错误)

    ​ 400: 语法错误

    ​ 401: 未认证

    ​ 403: 禁止访问

    ​ 404: 资源未找到

    5xx: 服务器错误

    ​ 500: 服务器故障

    ​ 503: 服务器繁忙

    返回XMLHttpRequest请求的当前状态(xhr.readyState)

    0: 未初始化(未调用open方法)

    1: 启动(已调用open方法,未调用send方法)

    2: 发送(已调用send方法,未接收到响应)

    3: 接收(已接收到部分响应数据)

    4: 完成(已接收到所有响应数据)

    插槽

    父元素传入的内容会显示在子组件的插槽处

    默认插槽

    在组件标签内传入元素,使用slot标签作为插槽,内容为默认值,有传入元素时会被替代

    // 父组件
    <template>
      <div>
        <hello-world @myEvent="getC">hello</hello-world>
      </div>
    </template>
    
    // 子组件
    <template>
      <div class="hello">
        <h1>{{ msg }}</h1>
        <p>
          <!-- 父组件没有传入数据时默认显示slot -->
          <slot>slot</slot>
        </p>
      </div>
    </template>
    

    具名插槽

    有多个插槽时,使用name属性命名,传入内容时使用slot=“”(新写法v-slot:)来指定插入的插槽

    // 父组件,注意写法
    <template>
      <div>
        <hello-world @myEvent="getC">
          <template v-slot:div>
            hello
          </template>
        </hello-world>
      </div>
    </template>
    
    // 子组件
    <template>
      <div class="hello">
        <p>
          <button @click="sendFa()">给父组件</button>
          <slot name="p"></slot>
        </p>
        <div>
          <slot name="div"></slot>
        </div>
      </div>
    </template>
    

    作用域插槽

    适用于data内容在子组件中时,可以像对组件传递 props 那样,向一个插槽的出口上传递 attributes

    // 父组件
    <template>
      <div>
        <hello-world @myEvent="getC">
          <template scope="data">
            {{ data.nm }}
          </template>
        </hello-world>
      </div>
    </template>
    
    // 子组件
    <template>
      <div class="hello">
        <p>
          <button @click="sendFa()">给父组件</button>
        </p>
        <div>
          插槽
          <slot :nm="nm"></slot>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: 'HelloWorld',
      data(){
        return{
          nm: ['mis','sd','op']
        }
      }
    }
    </script>
    
    
    slot-scope属性

    利用slot-scope="scope"的scope可以取出父元素所绑定的数据,例如 element-ui 中的 table 组件,在渲染表格行时我们经常需要用到 slot-scope 来获取当前行的数据

    <template>
      <el-table :data="tableData">
        <el-table-column label="序号">
          <template slot-scope="scope">
            <span>{{ scope.$index + 1 }}</span>
          </template>
        </el-table-column>
        <el-table-column label="姓名">
          <template slot-scope="scope">
            <span>{{ scope.row.name }}</span>
          </template>
        </el-table-column>
        <el-table-column label="年龄">
          <!-- 支持直接通过 {} 去解构数据 -->
          <template slot-scope="{row}">
            <span>{{ row.age }}</span>
          </template>
        </el-table-column>
      </el-table>
    </template>
    

    Vuex

    实现多组件共享数据,每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。每个应用将仅仅包含一个 store 实例。

    以下是vuex基本原理图

    在这里插入图片描述

    以下根据vux(store)包含内容一一介绍

    state:

    存储需要共享的数据

    getter:

    用于加工state中的属性,有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数,如果有多个组件需要用到此属性,Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)

    从 Vue 3.0 开始,getter 的结果不再像计算属性一样会被缓存起来

    const store = createStore({
      state: {
        todos: [
          { id: 1, text: '...', done: true },
          { id: 2, text: '...', done: false }
        ]
      },
      getters: {
        doneTodos (state) {
          return state.todos.filter(todo => todo.done)
        }
      }
    })
    

    mutation:

    更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:

    const store = createStore({
      state: {
        count: 1
      },
      mutations: {
        increment (state) {
          // 变更状态
          state.count++
        }
      }
    })
    

    不过你不能直接调用一个 mutation 处理函数。这个选项更像是事件注册:“当触发一个类型为 increment 的 mutation 时,调用此函数。”要唤醒一个 mutation 处理函数,你需要以相应的 type 调用 store.commit 方法:

    store.commit('increment')
    

    actions:

    Action 类似于 mutation,不同在于:

    • Action 提交的是 mutation,而不是直接变更状态。
    • Action 可以包含任意异步操作。

    让我们来注册一个简单的 action:

    const store = createStore({
      state: {
        count: 0
      },
      mutations: {
        increment (state) {
          state.count++
        }
      },
      actions: {
        increment (context) {
          context.commit('increment')
        }
      }
    })
    

    Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,实践中,我们会经常用到 ES2015 的参数解构来简化代码

    actions: {
      increment ({ commit }) {
        commit('increment')
      }
    }
    
    分发 Action

    Action 通过 store.dispatch 方法触发:

    store.dispatch('increment')
    

    由于mutation 必须同步执行这个限制,Action 不受约束,我们可以在 action 内部执行异步操作:

    actions: {
      incrementAsync ({ commit }) {
        setTimeout(() => {
          commit('increment')
        }, 1000)
      }
    }
    
    在组件中分发 Action

    你在组件中使用 this.$store.dispatch('xxx') 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store):

    import { mapActions } from 'vuex'
    
    export default {
      // ...
      methods: {
        ...mapActions([
          'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
    
          // `mapActions` 也支持载荷:
          'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
        ]),
        ...mapActions({
          add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
        })
      }
    }
    

    module

    当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

    为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

    const moduleA = {
      state: () => ({ ... }),
      mutations: { ... },
      actions: { ... },
      getters: { ... }
    }
    
    const moduleB = {
      state: () => ({ ... }),
      mutations: { ... },
      actions: { ... }
    }
    
    const store = createStore({
      modules: {
        a: moduleA,
        b: moduleB
      }
    })
    
    store.state.a // -> moduleA 的状态
    store.state.b // -> moduleB 的状态
    

    对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:

    const moduleA = {
      // ...
      getters: {
        sumWithRootCount (state, getters, rootState) {
          return state.count + rootState.count
        }
      }
    }
    
    命名空间

    默认情况下,模块内部的 action 和 mutation 仍然是注册在全局命名空间的——这样使得多个模块能够对同一个 action 或 mutation 作出响应。Getter 同样也默认注册在全局命名空间,必须注意,不要在不同的、无命名空间的模块中定义两个相同的 getter 从而导致错误。

    如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。例如:

    const store = createStore({
      modules: {
        account: {
          namespaced: true,
    
          // 模块内容(module assets)
          state: () => ({ ... }), // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响
          getters: {
            isAdmin () { ... } // -> getters['account/isAdmin']
          },
          actions: {
            login () { ... } // -> dispatch('account/login')
          },
          mutations: {
            login () { ... } // -> commit('account/login')
          },
    
          // 嵌套模块
          modules: {
            // 继承父模块的命名空间
            myPage: {
              state: () => ({ ... }),
              getters: {
                profile () { ... } // -> getters['account/profile']
              }
            },
    
            // 进一步嵌套命名空间
            posts: {
              namespaced: true,
    
              state: () => ({ ... }),
              getters: {
                popular () { ... } // -> getters['account/posts/popular']
              }
            }
          }
        }
      }
    })
    

    启用了命名空间的 getter 和 action 会收到局部化的 getterdispatchcommit。换言之,你在使用模块内容(module assets)时不需要在同一模块内额外添加空间名前缀。更改 namespaced 属性后不需要修改模块内的代码

    路由

    基于vue-router插件,vue2只能使用3版本及以下

    使用router-link标签的to属性实现跳转

    普通配置如下:

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import HomeView from '../views/HomeView.vue'
    
    Vue.use(VueRouter)
    
    const routes = [
      {
        path: '/',
        name: 'home',
        component: HomeView
      }{
        path: '/about',
        name: 'about',
        component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
      }
    ]
    
    const router = new VueRouter({
      routes
    })
    
    export default router
    
    

    根据所配置的路由跳转,所跳转的路由的组件内容就会显示在router-view标签处,而router-link标签则是通过to属性选择路由,点击这个标签进行路由跳转

    <template>
      <div id="app">
        <nav>
          <router-link to="/">Home</router-link> |
          <router-link to="/about">About</router-link>
        </nav>
        <router-view/>
      </div>
    </template>
    

    多级路由

    有路由嵌套是,可以使用路由中children属性配置子路由

    const router = new VueRouter({
      routes: [
        {
          path: '/user/:id',
          component: User,
          children: [
            {
              // 当 /user/:id/profile 匹配成功,
              // UserProfile 会被渲染在 User 的 
              path: 'profile',
              component: UserProfile
            },
            {
              // 当 /user/:id/posts 匹配成功
              // UserPosts 会被渲染在 User 的 
              path: 'posts',
              component: UserPosts
            }
          ]
        }
      ]
    })
    

    动态路由

    我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果:

    const User = {
      template: '
    User
    '
    } const router = new VueRouter({ routes: [ // 动态路径参数 以冒号开头 { path: '/user/:id', component: User } ] })

    在配置component的时候,需要动态导入组件,有两种导入方式,import和require方式

    • import属于es6导入方式,只支持静态导入,也就是说,你不能够使用变量或表达式,只能使用字符串。

    • require属于commonJS方式,可以支持动态的导入,但笔者尝试中,可以使用``模板字符串方式,貌似也无法直接使用变量。这里尽量用字符串拼接,而不是用变量去代替拼接的字符串

    // 字符串拼接方式
    component: import('@/view'+'/sys/user')
    // 模板字符串方式,webpack4+版本需使用require方式,注意,`@${item.curl}`,是不行的!必须指定一个目录,不能全用变量代替
    component: (resolve) => require([`@/view${item.curl}`], resolve),
    

    路由守卫

    对路由跳转添加限制,有to,from,next()三个参数,以前置路由守卫为例:

    router.beforeEach((to,from,next)=>{
      // to为要跳转的路由(目标),from为开始跳转的路由(起始),next()为允许路由跳转(放行)
      if(to.name === "login"){
        next();
      }
     }
    

    以下为限制在没有登陆时对用户路由跳转的限制:

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import Login from '../views/Login.vue'
    import MainBox from '@/views/MainBox.vue'
    import RoutesConfig from './config'
    import store from '@/store/index'
    
    Vue.use(VueRouter)
    
    const routes = [
      {
        path: "/login",
        name: "login",
        component: Login
      },
      {
        path: "/mainbox",
        name: "mainbox",
        component: MainBox
        // 嵌套路由根据权限动态添加
      },
      
    ]
    
    const router = new VueRouter({
      routes
    })
    
    
    // 路由拦截(路由守卫),路由跳转之前
    router.beforeEach((to,from,next)=>{
      if(to.name === "login"){
        next();
      }else{
        // 是否登陆
        if(!localStorage.getItem("token")){
          next({
            path: "login"
          })
        }else{
          if(!store.state.isGetFullRoute){
            // 重新加载所有路由,token刚添加路由还没加载完毕
            next({
              path: to.fullPath
            })
            AddRoutesConfig()
          }else{
            // 加载一遍后就不用再加载所有路由了,避免死循环
            next()
          }
        }
      }
    })
    
    const AddRoutesConfig = ()=>{
      RoutesConfig.forEach(item=>{
        router.addRoute("mainbox",item)
      })
      // 改变isGetFullRoute,代表路由已加载完毕
      store.commit("changeGetFullRoute",true);
    }
    
    
    export default router
    
    
    路由守卫分类
    全局守卫:

    前置路由守卫 beforeEach(to,from,next){}

    后置路由守卫 afterEach(to,from){}

    独享守卫(当个路由配置中配置):

    beforeEnter(to,from,nect){}

    组件内守卫(组件内利用配置项):

    通过路由规则进入前 beforeRouteEnter(to,from,next){}

    通过路由规则离开前 beforeRouteLeave(to,form,next){}

    路由的props属性

    props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
    children: [
        {
            name: 'demo',
            path:'demo',
            component: Demo,
            // props对象中所有的key-value的组合最终都会通过props传给Demo组件
            props:{
                id: '1',
                title: 'hello'
            }
        }
    ]
    

    在Demo组件中使用props接收传递来的数据

    <template>
      <div>
        <ul>
            <li>接收的id为{{id}}</li>
            <li>接收的title为{{title}}</li>
        </ul>
      </div>
    </template>
     
    <script>
    export default {
        name: 'Demo',
        // 接收组件传递来的数据
        props: ['id', 'title']
    }
    </script>
    
    props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Demo组件(只能作用于params类型的参数)
    {
        name: 'demo',
        path:'demo/:id/:title', //使用占位符声明接收params参数
        component: Demo,
        // 第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
        props: true
    }
    

    传递params参数

    <li v-for="item in list" :key="item.id">
      <router-link :to="{
        name: 'demo',
        params: {
          	id: item.id,
          	title: item.title
        }
      	}">
    	{{ item.title }}
    	</router-link>
    </li>
    

    Demo组件接收参数

    <template>
      <div>
        <ul>
            <li>接收的id为{{id}}</li>
            <li>接收的title为{{title}}</li>
        </ul>
      </div>
    </template>
     
    <script>
    export default {
        name: 'Demo',
        // 接收组件传递来的数据
        props: ['id', 'title']
    }
    </script>
    
    props值为函数,该函数返回的对象中每一组key-value都会通过props传给Demo组件
    {
        name: 'demo',
        path:'demo/:id/:title', //使用占位符声明接收params参数
        component: Demo,
        // 第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
        // props函数会自动调用并提供一个$route参数 可以通过$route来获取想要的数据传递给组件
        props($route) {
            return {
                id: $route.params.id,
                title: $route.params.title,
                // 还可以返回一些别的数据
                a: 1,
                b: "hello"
            }
        }
    }
    

    Demo组件使用props接收参数

    <template>
      <div>
        <ul>
            <li>接收的id为{{id}}</li>
            <li>接收的title为{{title}}</li>
            {{a}}-----{{b}}
        </ul>
      </div>
    </template>
     
    <script>
    export default {
        name: 'Demo',
        // 接收组件传递的参数
        props: ['id', 'title', 'a', 'b']
    }
    </script>
    

    路由跳转方法

    this.$router.push({path/name:‘’,query/param:{}}) 带参数根据路径或路由名跳转

    this.$router.replace() 与上面push用法一致

    this.$router.forward() 路由前进一级

    this.$router.back() 路由后退一级

    this.$router.go() 路由前进(n)或后退(-n) n级

    缓存路由组件

    使组件保持挂载,被切换时不被销毁

    
    
      
    
    

    默认会缓存内部的所有组件实例,但我们可以通过 include 来定制该行为

    <!-- 以英文逗号分隔的字符串 -->
    <KeepAlive include="a,b">
      <component :is="view" />
    </KeepAlive>
    
    <!-- 正则表达式 (需使用 `v-bind`) -->
    <KeepAlive :include="/a|b/">
      <component :is="view" />
    </KeepAlive>
    
    <!-- 数组 (需使用 `v-bind`) -->
    <KeepAlive :include="['a', 'b']">
      <component :is="view" />
    </KeepAlive>
    

    路由器两种工作模式

    hash模式

    如:https://element.eleme.cn/#/zh-CN/component/installation

    在url中#后的内容为hash值,hash值不会包含在http请求中(带给服务器),兼容性较好

    history模式

    如:https://cn.vuejs.org/guide/introduction.html

    会将所有url内容带给服务器,会造成404,需要进行过滤配置

  • 相关阅读:
    C++ STL容器底层实现原理
    Flutter组件--TabBar使用详情(分段控制器)
    ARM 版 OpenEuler 22.03 部署 KubeSphere v3.4.0 不完全指南
    IDEA小技巧:Markdown里的命令行可以直接运行了
    再思考设计模式
    Job for redis-server.service failed because the control process exited with error code(Centos 7 设置Redis开机自启报错)
    一文了解 Go time 包的时间常用操作
    数据结构(线性表习题1)
    K8S安装过程十:Kubernetes CNI插件与CoreDNS服务部署
    G1D4-软考中级《项目集成管理》-下午真题
  • 原文地址:https://blog.csdn.net/qq_53922901/article/details/140350679