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属性
- 功能:可以把多个组件共用的配置提取成一个混入对象
- 使用方式:
{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
- 语法:
this.$nextTick(回调函数)
- 作用:在下一次 DOM 更新结束后执行其指定的回调。
- 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。
WebStorage
浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
- sessionStorage存储的内容会随着浏览器窗口关闭而消失。
- 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
- 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>
- 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>
- About组件
<template>
<div class="about">
<h2>{{ msg }}</h2>
</div>
</template>
<script>
export default {
name: 'About',
data(){
return{
msg: 'About子组件'
}
}
}
</script>
- 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
}
]
}
]
})
- 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 的入口,通常只在以下情况下使用:
- 需要在非单文件组件中使用组合式 API 时。
- 需要在基于选项式 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:基础配置,告诉我们项目的信息(版本号、项目姓名、依赖)