Vue基础知识

Vue基础知识

初识Vue

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10"></script>
</head>

<body>
    <div id="root">
        <h1>{{ name }}</h1><hr/>
        <a v-bind:href="url">百度一下</a><hr/>
        <p>{{ message }}</p>
        <input v-model:value="message"><hr/>
        <button @click = "openWindow">点我弹窗</button>
        <button @click = "sayHello($event, '你好啊')">说你好</button> 
    </div>
</body>
<script>
    const vm = new Vue({
        el: "#root",
        data(){
            return {
                name: "Hello Vue",
                url: "http://www.baidu.com",
                message: "表单双向绑定"
            }
        },
        // data: {
        //     name: "Hello Vue",
        //     url: "http://www.baidu.com",
        //     message: "表单双向绑定"
        // },
        methods:{
            openWindow(){
                alert("方法调用");
            },
            sayHello(event, arg){
                alert(arg);
            }
        }
    });       
</script>
</html>

MVVM模型

  • M:模型(Model):对应 data 中的数据
  • V:视图(View) :模板
  • VM:视图模型(ViewModel):Vue 实例对象

当一个 Vue 实例被创建时,它将 data 对象中的所有的 property 加入到 V。

vue 的响应式系统中。当这些 property 的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。

// 我们的数据对象
var data = { a: 1 }

// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
  data: data
})
// 获得这个实例上的 property
// 返回源数据中对应的字段
vm.a == data.a // => true

// 设置 property 也会影响到原始数据
vm.a = 2
data.a // => 2
// ……反之亦然
data.a = 3
vm.a // => 3

除了数据 property,Vue 实例还暴露了一些有用的实例 property 与方法。它们都有前缀 $,以便与用户定义的 property 区分开来。例如:

var data = { a: 1 }
var vm = new Vue({
  el: '#example',
  data: data
})

vm.$data === data // => true
vm.$el === document.getElementById('example') // => true

// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {
  // 这个回调将在 `vm.a` 改变后调用
})

响应式的实现原理

// 监测对象改变的原理
    let data = {
        name : "张三",
        age: 18
    }

    let obj = new Observe(data);
    console.log(obj);

    let vm = {};
    vm._data = data = obj;
    console.log(vm);
    
    function Observe(obj){
        const keys = Object.keys(obj);
        keys.forEach((k) =>{
            Object.defineProperty(this, k ,{
                get(){
                    return obj[k];
                },
                set(val){
                    obj[k] = val;
                }
            })
        })
    }

Vue指令

v-bind指令

用于绑定属性

<a v-bind:href="url">百度一下</a>
//可以简写为<a :href="url">百度一下</a>

v-text指令

<span v-text="msg"></span>等价于<span>{{msg}}</span>

v-html指令

双大括号的方式会将数据解释为纯文本,而非HTML。为了输出真正的HTML,可以用v-html指令

v-model 指令: 实现表单输入和应用状态之间的双向绑定。

<div id="app">
    <p>{{ message }}</p>
    <input v-model="message"> 
</div>  

v-model修饰符

.lazy
默认情况下,v-model同步输入框的值和数据。可以通过这个修饰符,转变为在change事件再同步。

<input v-model.lazy="msg">

.trim
自动过滤用户输入的首尾空格

<input v-model.trim="msg">

v-cloak指令

能够解决插值表达式闪烁的问题

v-on指令

主要用来监听dom事件,以便执行一些代码块。 可以给绑定的回调事件传递参数。

<a v-on:click="doSomething">...</a>

<!-- 缩写 -->
<a @click="doSomething">...</a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>

事件修饰符

.stop 阻止事件继续传播
.prevent 阻止默认行为
.capture 使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理
.self 只当在 event.target 是当前元素自身时触发处理函数
.once 事件将只会触发一次

事件冒泡:当一个元素接收到事件的时候 会把他接收到的事件传给自己的父级,一直到window

v-if指令、v-show指令

v-if:每次都会重新创建或删除元素

v-show:只是切换了display样式

<h3 v-if="flag">这是用v-if控制的元素</h3>
<h3 v-show="flag">这是用v-show控制的元素</h3>

v-for 指令

基于源数据多次渲染元素或模板块:

<div v-for="item in items">
  {{ item.text }}
</div>

另外也可以为数组索引指定别名 (或者用于对象的键):

<div v-for="(item, index) in items"></div>
<div v-for="(val, key) in object"></div>
<div v-for="(val, name, index) in object"></div>

v-for 的默认行为会尝试原地修改元素而不是移动它们。要强制其重新排序元素,你需要用特殊 attribute key 来提供一个排序提示:

<div v-for="item in items" :key="item.id">
  {{ item.text }}
</div>

自定义指令

/ 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})
<div id = "app">
	<p v-color='red'>我是红色</p>
</div>

Vue.directive('color', {
	bind : function(el, obj){
		el.style.color = obj.value; 
	}
})

如果想注册局部指令,组件中也接受一个 directives 的选项:

directives: {
  focus: {
    // 指令的定义
    inserted: function (el) {
      el.focus()
    }
  }
}

Vue属性

computed属性

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10"></script>
</head>

<body>
    <div id="root">
        {{ info }} 
    </div>
</body>
<script>
    const vm = new Vue({
        el: "#root",
        data(){
            return {
                name: "Hello Vue",
                age: 18
            }
        },
        computed:{
            // info:{
            //     get(){
            //         return this.name + this.age;
            //     }
            // }
            info(){
                return this.name + this.age;
            }
        }
    });       
</script>
</html>

watch属性

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10"></script>
</head>

<body>
    <div id="root">
        <p>{{ message }}</p>
        <input v-model:value="message">
    </div>
</body>
<script>
    const vm = new Vue({
        el: "#root",
        data(){
            return {
                message: "表单双向绑定"
            }
        },
        watch:{
            message:{
                handler(newValue, oldValue){
                    console.log(newValue);
                    console.log(oldValue);
                }
            }
        }
        // watch:{
        //     message(newValue, oldValue){
        //         console.log(newValue);
        //         console.log(oldValue); 
        //     }
        // }
    });
    vm.$watch('message',{
    	handler(newValue, oldValue){
            console.log(newValue);
            console.log(oldValue);
        }
    }) 
</script>
</html>

深度监视

Vue中的watch默认不监测对象内部值的改变,配置deep:true可以监测对象内部值的改变

<script>
    let vm = new Vue({
        el : "#app",
        data(){
            return{
                number: {a: 1, b: 2}
            }
        },
        watch:{
            number:{
                deep: true,
                handler(){
            		console.log("内部值改变");	        
                }
            }
        }
    })

</script>

computed与watch区别

1、computed能完成的功能,watch都可以完成

2、watch能完成的功能,computed不一定能完成,例如watch可以进行异步操作

props属性

<!-- HelloWorld.vue-->
<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  }
}
</script>

<!-- App.vue-->
<template>
    <div id="app">
      <HelloWorld msg="Hello Vue"/>
    </div>
  </template>
  
  <script>
  import HelloWorld from './components/HelloWorld'
  export default {
    name: 'App',
    components: {
      HelloWorld
    }
  }
  </script>

mixins属性

  1. 功能:可以把多个组件共用的配置提取成一个混入对象
  2. 使用方式:{data:{...},methods:{}},使用时,全局混入:Vue.mixin(xxx) 局部混入:mixins:['xxx']
<template>
  <div class="hello">
    <button @click="click">点我弹窗</button>
  </div>
</template>

<script>
import { mixin } from '../mixin';
export default {
  name: 'HelloWorld',
  data(){
    return{
      message: "mixin混入"        
    }
  },
  props:{
    msg: String
  },
  mixins:[mixin]
}
</script>

在mixin文件中定义

export const mixin = {
  methods: {
    click(){
      alert(this.message);
    }  
  }
}

ref属性

ref属性被用来给元素或者子组件注册引用信息

<template>
    <div id="app">
      <p ref="title">测试</p>
      <button @click="getTest">获取测试</button>
    </div>
  </template>
  
  <script>
  export default {
    name: 'App',
    methods:{
      getTest(){
        console.log(this.$refs.title);
      }
    }
  }
  </script>

CLass与Style绑定

操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因为它们都是 attribute,所以我们可以用 v-bind 处理它们:只需要通过表达式计算出字符串结果即可。不过,字符串拼接麻烦且易错。因此,在将 v-bind 用于 class 和 style 时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。

对象语法

<div v-bind:class="{ active: true, 'text-danger': false}"></div>

还可以写成

<div v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>

data: {
  isActive: true,
  hasError: false
}

绑定的数据对象不必内联定义在模板里:

<div v-bind:class="classObject"></div>

data: {
  classObject: {
    active: true,
    'text-danger': false
  }
}

数组语法

我们可以把一个数组传给 v-bind:class,以应用一个 class 列表:

<div v-bind:class="[activeClass, errorClass]"></div>

data: {
  activeClass: 'active',
  errorClass: 'text-danger'
}

渲染为:

<div class="active text-danger"></div>

还可以直接写为:

<div v-bind:class="['active','text-danger']"></div>

style样式绑定

<p style= "{color : 'red'}">这是个段落</p>

Vue过渡/动画

链接

过滤器

Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示:

// 定义一个Vue的全局过滤器
Vue.filter('msgFormat', function (msg, arg) {
	return msg.replace(/卑鄙/g, arg);
});

<p>{{msg | msgFormat('高尚')}}</p>

局部过滤器

filters: {
  capitalize: function (value) {
    if (!value) return ''
    value = value.toString()
    return value.charAt(0).toUpperCase() + value.slice(1)
  }
}

生命周期

实例生命周期钩子

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会

  • 创建

beforeCreate

created

beforecreated: el和data并未初始化 created:完成了data 数据的初始化,el没有

  • 挂载

beforeMount

mounted

beforeMount:完成了el和 data初始化,mounted :完成挂载

  • 更新

beforeUpdate

updated

  • 销毁

beforeDestroy

Destroyed

Vue中常用钩子函数

created: 完成data数据创建

mounted: 发送ajax请求,启动定时器,绑定自定义时间,定义消息等【初始化操作】完成初始渲染。

beforeDestroy: 消除定时器,解绑自定义事件,取消订阅消息等【收尾工作】

注意点: vue生命周期钩子不能使用箭头函数

function函数: 钩子中的this指向vue实例

箭头函数: 钩子中的this指向window

Vue脚手架

vue-cli

  • 安装脚手架

npm install -g @vue/cli

  • 创建项目

vue create my-project

  • 启动项目

npm run serve

组件

组件基础

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10"></script>
</head>

<body>
    <div id = "app">
        <school></school>
    </div>
</body>
<script>
    const school = Vue.extend({
        name: "school",
        template: '<p>{{ message }}</p>',
        data(){
            return{
                message: "组件"
            }
        }
    })
    let vm = new Vue({
        el : "#app",
        components:{
            school
        }
    })

</script>
</html>

school组件本质上是一个名为VueComponent的构造函数,当我们写时,Vue解析时会帮我们创建school组件的实例对象。

这里还有一个Vue组件的示例:

// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

组件是可复用的 Vue 实例,且带有一个名字:在这个例子中是 <button-counter> 。我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用:

<div id="components-demo">
  <button-counter></button-counter>
</div>

new Vue({ el: '#components-demo' })

因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

data 必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝。

组件之间通信的方式

props父组件向子组件传值

在子组件中使用props定义组件的属性,然后在父组件之中绑定属性。

// 在父组件中绑定name和父组件的age
<my-vue name="张三" :age="age"></my-vue>

//在子组件中使用props就可以获取
export default {
  name: 'MyVue'
  props:['name', 'age']
}

子组件向父组件传值(通过自定义事件形式)

在父组件之中定义一个方法,绑定在子组件之中使用props定义组件的属性,当子组件的属性被触发时,回调执行父组件的方法。

<!-- App.vue -->
<template>
    <div id="app">
      <HelloWorld :getSon="getSon"></HelloWorld>
    </div>
  </template>
  
<script>
  import HelloWorld from './components/HelloWorld'
  export default {
    name: 'App',
    components: {
      HelloWorld
    },
    methods:{
      getSon(name){
        console.log(name);  
      }
    }
  }
</script>
<!-- HelloWorld.vue -->
<template>
  <div class="hello">
    <button @click="sendName">子传父</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props:['getSon'],
  data(){
    return{
      msg : "孩子的礼物"  
    }
  },
  methods:{
    sendName(){
      this.getSon(this.msg);
    }
  }
}
</script>

在父组件之中绑定一个事件 v-on:test='demo',事件名是test,在子组件之中触发this.$emit('test',data)可以传值,回调函数是在父组件之中的demo。在父组件之中还可以将绑定事件写在mounted生命周期方法中,不使用标签的形式,this.$refs.son.$on('test', this.demo)。解绑自定义事件可以用this.$off('test')

<!-- 在父组件之中绑定一个事件名test,在子组件之中触发,父组件就可以调用demo获取到子组件传来的参数 -->
<template>
    <div id="app">
      <HelloWorld v-on:test="getSon"></HelloWorld>
      {{ message }}
    </div>
  </template>
  
<script>
  import HelloWorld from './components/HelloWorld'
  export default {
    name: 'App',
    components: {
      HelloWorld
    },
    data(){
      return{
        message: "HelloWorld"
      }
    },
    methods:{
      getSon(name){
        console.log(name);  
      }
    }
  }
</script>
<!-- 子组件 -->
<template>
  <div class="hello">
    <button @click="sendName">子传父</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data(){
    return{
      msg : "孩子的礼物"  
    }
  },
  methods:{
    sendName(){
      this.$emit('test',this.msg);
    }
  }
}
</script>

任意组件之间传值

使用总线的方式

在发送数据的一方使用this.$bus.$emit('事件名', data);,在接受数据的一方使用this.$bus.$on('事件名', (data) => {})

使用发布-订阅机制

导入pubsub-js,发送方pubsub.publish('事件名', data),接收方使用 pubsub.subscribe('事件名',回调函数)

// 在第一个组件中使用,触发事件
methods: {
	sendName(){
		this.$bus.$emit('hello', this.message);
        // 发布消息
   		pubsub.publish('hello', this.message)
      }
}

// 在第二个组件中使用
mounted() {
    this.$bus.$on('hello', (data) =>{
    	console.log("我收到了兄弟组件的数据" + data);
    })
    this.pudId = pubsub.subscribe('hello',(msg, data) => {
        console.log(this);
        console.log("我收到了兄弟组件的数据" + data);
    })
},
beforeDestroy() {
	this.$bus.$off('hello');
	pubsub.unsubscribe(this.pudId)
}

使用vuex

插槽

默认插槽

在父组件之中使用组件时,<A><h1>Content</h1></A>,在组件之中的template之中就可以使用<slot></slot>就可以显示内容了。

<!-- App.vue -->  
<template>
    <div id="app">
      <HelloWorld>
        <h1>插槽</h1>  
      </HelloWorld>
      {{ message }}
    </div>
  </template>
  
  <script>
  import HelloWorld from './components/HelloWorld'
  
  export default {
    name: 'App',
    components: {
      HelloWorld
    },
    data(){
      return{
        message: "父组件"
      }
    }
  }
  </script>
<!-- HelloWorld -->  
<template>
  <div class="hello">
    {{ msg }}
    <slot></slot>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data(){
    return{
      msg: "子组件"
    }
  }
}
</script>

具名插槽

就是给插槽取一个名字

<!-- App.vue -->  
<template>
  <div class="hello">
    {{ msg }}
    <slot name="header"></slot>
    <slot name="middle"></slot>
    <slot name="footer"></slot>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data(){
    return{
      msg: "子组件"
    }
  }
}
</script>
<!-- HelloWorld -->  
<template>
    <div id="app">
      <HelloWorld>
        <h1 slot="header">头部</h1>
        <template v-slot:middle>中间</template>
        <h1 slot="footer">尾部</h1>
      </HelloWorld>
      {{ message }}
    </div>
  </template>
  
  <script>
  import HelloWorld from './components/HelloWorld'
  
  export default {
    name: 'App',
    components: {
      HelloWorld
    },
    data(){
      return{
        message: "父组件"
      }
    }
  }
  </script>

作用域插槽

作用域插槽就是子组件可以给父组件传参,父组件决定怎么展示。

<!-- App -->  
<template>
    <div id="app">
      <HelloWorld>
        <template scope="msg">
          {{ msg.data }}
        </template>
      </HelloWorld>
    </div>
  </template>
  
<script>
  import HelloWorld from './components/HelloWorld'
  
  export default {
    name: 'App',
    components: {
      HelloWorld
    },
    data(){
      return{
        message: "作用域插槽"
      }
    }
  }
</script>

<!-- HelloWorld -->  
<template>
  <div class="hello">
    <slot :data="msg"></slot>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data(){
    return{
      msg: "子组件"
    }
  }
}
</script>

其他

nextTick

  1. 语法:this.$nextTick(回调函数)
  2. 作用:在下一次 DOM 更新结束后执行其指定的回调。
  3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。

WebStorage

浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。

  1. sessionStorage存储的内容会随着浏览器窗口关闭而消失。
  2. localStorage存储的内容,需要手动清除才会消失。

Vuex

在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。当多个组件共享数据时使用。

基本使用案例

store/index.js

import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
export default new Vuex.Store({
    /*
    state:保存具体的数据
    getters:类似于计算属性
    actions:响应组件中用户的动作
    mutations:执行具体的数据修改
    */
    state:{
        sum:20
    },
    getters:{
        bigSum(state){
            return state.sum * 10;
        }
    },
    actions:{
        add(context, value){
            context.commit('ADD', value);
        }
    },
    mutations:{
        ADD(state, value){
            state.sum += value;
        },
    }
})

main.js

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

HelloWorld.vue

<template>
  <div class="hello">
    <h2> {{ $store.state.sum }} </h2>
    <h2> {{ $store.getters.bigSum }} </h2>
    <button @click="clickAdd">+1操作</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data(){
    return{
      num: 1
    }
  },
  methods:{
    clickAdd(){
      this.$store.dispatch('add', this.num);
    }
  }
}
</script>

mapMutations和mapMutations

可以直接映射,写在computed中,数据就可以直接使用

mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。

HelloWorld.vue

<template>
  <div class="hello">
    <h2> {{ sum }} </h2>
    <h2> {{ bigSum }} </h2>
    <button @click="clickAdd(num)">Actions+1操作</button>
    <button @click="clickSub(num)">Mutation-1操作</button>
  </div>
</template>

<script>
import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'
export default {
  name: 'HelloWorld',
  data(){
    return{
      num: 1
    }
  },
  methods:{
    ...mapActions({clickAdd:'add'}),
    ...mapMutations(['clickSub'])
  },
  computed:{
    ...mapState(['sum']),
    ...mapGetters(['bigSum'])
  },
  mounted(){
    console.log(this);
  }

}
</script> 

store/index

import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
export default new Vuex.Store({
    state:{
        sum:20
    },
    getters:{
        bigSum(state){
            return state.sum * 10;
        }
    },
    actions:{
        add(context, value){
            context.commit('ADD', value);
        }
    },
    mutations:{
        ADD(state, value){
            state.sum += value;
        },
        clickSub(state, value){
            state.sum -= value;
        }
    }
})

当数据比较多时,可以采用模块化的写法

const store1 = {
  namespaced:true,//开启命名空间
  state:{},
  mutations: { ... },
  actions: { ... },
  getters: {}
}
const store2 = {
  namespaced:true,//开启命名空间
  state:{},
  mutations: { ... },
  actions: { ... },
  getters: {}
}
const store = new Vuex.Store({
  modules: {
    store1,
    store2
  }
})              

Vue-Router

声明式路由

案例Demo

  1. App.vue
<template>
    <div id="app">
      {{ message }}
      <hr/>
      <router-link to="/home">Home组件</router-link>
      <router-link to="/about">About组件</router-link>
      <router-view></router-view>  
    </div>
  </template>
  
  <script>
  export default {
    name: 'App',
    data(){
      return{
        message: "父组件"
      }
    }
  }
  </script>

  1. Home
<template>
  <div class="home">
    <h2>{{ msg }}</h2>
    <router-link to="/home/science">Science组件</router-link>
    <router-link to="/home/news">News组件</router-link>
    <router-view></router-view>  
  </div>
</template>

<script>
export default {
  name: 'Home',
  data(){
    return{
      msg: 'Home组件'
    }
  }
}
</script>

  1. About组件
<template>
  <div class="about">
    <h2>{{ msg }}</h2>  
  </div>
</template>

<script>
export default {
  name: 'About',
  data(){
    return{
      msg: 'About子组件'
    }
  }
}
</script>

  1. rorter/index.js
import VueRouter from 'vue-router'
import About from '../page/About.vue'
import Home from '../page/Home.vue'
import Science from '../page/Science.vue'
import News from '../page/News.vue'
import Vue from 'vue'
Vue.use(VueRouter)
export default new VueRouter({
    mode: "history",
    routes: [
        {
            path: "/about",
            component: About
        },
        {
            path: "/home",
            component: Home,
            children:[
                {
                    path: "science",
                    component: Science
                },
                {
                    path: "news",
                    component: News
                }
            ]
        }
    ]
})

  1. main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store/index'
import router from './router/index'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  store,
  router
}).$mount('#app')

每个组件都有都有自己的$route属性,里面存着自己的路由信息

整个应用只有一个router,可以通过组件的$router属性获取到

编程式路由

不借助router-link实现路由跳转,让路由跳转更加灵活

this.$router.push({
	name:'router1',
	params:{
		id:xxx,
		title:xxx
	}
})
this.$router.replace({
	name:'router1',
		params:{
			id:xxx,
			title:xxx
		}
})
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go() //可前进也可后退

缓存路由组件

让不展示的路由组件保持挂载,不被销毁。

// 缓存About组件-组件名
<keep-alive include="About">
	<router-view></router-view>
</keep-alive>

使用编程式路由$router.push({name: 'home'},query:{},params:{})时会返回一个Promise对象

vue路由传值

案例Demo

<template>
  <div class="home">
    <h2>{{ msg }}</h2>
    <router-link :to="{name:'science',params:{title:'paramsScience'}}">
      Science组件
    </router-link>
    <router-link to="/home/news?title=queryNews">News组件</router-link>
    <router-view></router-view>  
  </div>
</template>

query传值

第一种是to的字符串写法

router-link :to="/home?id=1001&title=Hello"

第二种是to的对象写法

router-link :to="{path:'/',query:{id:1001,title:'Hello'}}",如果使用了命名路由,可以将path直接换成name:''

params传值

如果想要params参数可传可不传,定义路由时path:/path/:keywords?,增加?即可

如果params传递的是’',可以使用

字符串写法

router-link :to="/home/1001/Hello",然后在路由配置里面配置{path:page/:id/:title}声明接收参数

对象写法

router-link :to="{name:'/',params:{id:1001,title:'Hello'}}",不允许使用path

使用{{this.$route.query.id}}或者{{this.$route.params.id}}在子路由中接收参数即可

当属性过多时,使用上面的写法读取不可取,所以可以使用props

props传值

props的第一种写法,在路由规则配置那里props:{a:1},然后在其所对应的路由之中就可以使用props:['a']来接收数据

props的第二种写法,在路由规则配置那里props:true,就会把该路由组件收到的所有params参数,以props的形式传给该组件

props的第三种写法,值为函数,在路由规则配置那里props($route){return {id:$route.query.id,title:$route.query.title}}

// 1、使用params传值的方式案例
// home组件跳转到about组件,在about组件中可以使用this.$route.params获取到参数
this.$router.push(
	{
		name:"about",
		params:{
			id: '102',
			title: "HomeTitle"
		}
}
// props方式案例
// 在定义路由规则时,增加props属性
{
	name: 'about',
	path: "/about",
	component: About
    props:{a:1003}
}    
    

router-link的属性

  • replace: 设置 replace 属性的话,当点击时,会调用 router.replace() 而不是 router.push(),于是导航后不会留下 history记录

路由守卫

// index.js中
// 全局前置路由守备--初始化被调用/切换路由之前调用
router.beforeEach((to, from, next) =>{
    next();
})
// 后置路由守卫--初始化被调用/切换路由之后调用
router.afterEach((to, from) =>{
   
})
// 配置路由规则时--独享路由守卫
beforeEnter(to,from,next){
}
// 组件内守卫
// 进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {}
// 离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {}

路由的两种工作模式:hash模式与history模式

Vue3

Composition组合API

setup

setup() 钩子是在组件中使用组合式 API 的入口,通常只在以下情况下使用:

  1. 需要在非单文件组件中使用组合式 API 时。
  2. 需要在基于选项式 API 的组件中集成基于组合式 API 的代码时。

对于结合单文件组件使用的组合式 API,推荐通过

Demo示例

<!--HelloWorld-->
<template>
  {{ message }}
  <hr/>
  {{ count }}
  <div class="card">
    <button @click="count++">点我+1</button>
  </div>
</template>

<script>
  import { ref, toRef } from 'vue'
  export default{
    setup(props, context) {
      const count = ref(0);
      const message = toRef(props, 'msg');  
      return{
        count, message
      }
    },
    props: {
      msg: String
    }
  }
</script>

<!--App.vue-->
<script setup>
import HelloWorld from './components/HelloWorld.vue'
</script>

<template>
  <HelloWorld msg="Hello Vue3" />
</template>

ref、shallowRef

  • ref

定义一个响应式的数据,const obj = ref(value),创建一个包含响应式的数据的引用对象(reference)对象。

  • shallowRef

shallowRef() 常常用于对大型数据结构的性能优化或是与外部的状态管理系统集成

reactive与shallowReactive

  • reactive

定义一个对象类型的响应式数据,返回一个代理对象,它所定义的数据是"深层次"。

  • shallowReactive

reactive()不同,这里没有深层级的转换:一个浅层响应式对象里只有根级别的属性是响应式的。属性的值会被原样存储和暴露,这也意味着值为ref的属性不会被自动解包了

<template>
  {{ count }}
  {{ person.school.schoolName }}
  {{ person.name }}

  <div class="card">
    <button @click="count++">点我+1</button>
  </div>
</template>

<script>
  import { ref, toRef, shallowRef, computed, shallowReactive } from 'vue'
  export default{
    setup(props, context) {
      const count = ref(0);
      const person = shallowReactive({
        name: "张三",
        age: 18,
        school:{
          schoolName: "大学名称"
        }
      });
      return{
        count, message, person
      }
    }
  }
</script>

computed

setup(props, context){
    let sum = ref(0);
    let person = reactive({
            firstName: "张",
            lastName: "三",
            age: 18
    });
    // 计算属性
    person.fullName = computed(() =>{
        return person.firstName + person.lastName;
    })
    person.fullName = computed({
            get(){
                return person.firstName + person.lastName;
            },
            set(value){}
    });
}

watch

watch()默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数

setup(props, context){
    let sum = ref(0);
    // 监听sum属性
    watch(sum,(newValue, oldValue) =>{});
  	// 立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数
    watchEffect(()=>{
        this.sum++;
    })
}

vue3实现响应式的原理

let person = {
	name: 'zhangsan',
	age: 18
}; 
const p = new Proxy(person,{
	get(target, propName){
		console.log('读取属性'+ propName);
        // return target[propName];
        Reflect.get(target, propName)
	},
	set(target, propName, value){
		console.log("修改/新增属性" + propName);
		// target[propName] = value;
		Reflect.set(target, propName, value)
	},
	deleteProperty(target, propName){
		console.log("删除属性" + propName);
		// return delete target[propName];
		Reflect.deleteProperty(target, propName)
	}
}) 

生命周期函数

onMounted()

注册一个回调函数,在组件挂载完成后执行

onUpdated()

注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用

onUnmounted()

注册一个回调函数,在组件实例被卸载之后调用

依赖注入

provide()

提供一个值,可以被后代组件注入

inject()

注入一个由祖先组件或整个应用 (通过 app.provide()) 提供的值。

// 在父组件中提供之注入的值
const DI = ref("依赖注入")
provide('DI', DI)

// 在子组件中就可以得到
const DI = inject('DI');
console.log("注入数据@@" + DI.value);

自定义hook函数

Pinia

案例

store/index.js

import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
  state: () => {
    return { count: 10 }
  },
  actions: {
    increment() {
      this.count++
    },
  },
})

在组件中使用

<template>
  {{ counter.count }}
  <div class="card">
    <button @click="piniaAdd">Pinia+1</button>
  </div>
</template>

<script setup>
  import {useCounterStore} from '../store/index'
    function piniaAdd(){
      // counter.increment()
      counter.count++;
      //counter.$patch({ count: counter.count + 1 })  
    }
</script>

vue项目目录结构

node_modules

npm加载项目的依赖模块

src:源码目录

  • assets:静态资源存放目录。

  • components:组件存放目录,项目的公共组件可以存放在此供其他地方使用

  • router: 路由,此处配置项目路由

  • store: 状态管理

  • view: 路由页面组件

  • App.vue:是项目的主组件,页面的入口文件

  • main.js:默认为整个项目的入口文件

其他文件

  • babel.config.js:babel作为转译工具使我们的JavaScript代码正常运行在旧的或不支持新语法和api的环境
  • package.json:每个项目的根目录下面,一般都有一个package.json文件,定义了这个项目所需要的各种模块,以及项目的配置信息(比如名称、版本、许可证等元数据)
  • package-lock.json:基础配置,告诉我们项目的信息(版本号、项目姓名、依赖)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值