一:Vue基础
1:vue生命周期和生命周期钩子函数?
beforecreated:在实例初始化之后,el 和 data 并未初始化(这个时期,this变量还不能使用,在data下的数据,和methods下的方法,watcher中的事件都不能获得到;)
created:完成了 data 数据的初始化,el没有(这个时候可以操作vue实例中的数据和各种方法,但是还不能对"dom"节点进行操作;)
beforeMount:完成了 el 和 data 初始化这里的el是虚拟的dom;
mounted :完成挂载,在这发起后端请求,拿回数据,配合路由钩子做一些事情(挂载完毕,这时dom节点被渲染到文档内,一些需要dom的操作在此时才能正常进行),定义定时器
beforeUpdate:数据是新的,但是页面是旧的
update:数据和页面保持同步了,
beforeDestory: 你确认删除定时器吗?
destoryed :当前组件已被删除,(其实就是页面的退出)
A、什么是vue生命周期?
Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。
B、vue生命周期的作用是什么?
它的生命周期有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。
C、vue生命周期总共有几个阶段?
它可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后
D、第一次页面加载会触发哪几个钩子?
第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子
E、DOM 渲染在 哪个周期中就已经完成?
DOM 渲染在 mounted 中就已经完成了。
F、简单描述每个周期具体适合哪些场景?
1.beforecreate:可以在加个loading事件,在加载实例是触发
2.created:初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用
3.mounted:异步请求,启动定时器,绑定自定义事件,订阅消息
4.updated:如果对数据统一处理
5.beforeDestroy:清除定时器,解绑自定义事件,取消订阅,一般不再这里在操作数据,即使操作了,页面也不会更新了
2:v-show与v-if的区别
v-show是css切换,v-if是完整的销毁和重新创建
使用频繁切换时用v-show,运行时较少改变时用v-if
V-if=’false’v-if是条件渲染,当false的时候不会渲染
使用v-if的时候,如果值为false,那么页面将不会有这个html标签生成
v-show则是不管值是为true还是false,html元素都会存在,只是css中的display显示或隐藏
v-show 仅仅控制元素的显示方式,将 display 属性在 block 和 none 来回切换;而v-if会控制这个 DOM 节点的存在与否。当我们需要经常切换某个元素的显示/隐藏时,使用v-show会更加节省性能上的开销;当只需要一次显示或隐藏时,使用v-if更加合理。
3:开发中常用的指令有哪些?
v-model:一般用在表达输入,很轻松的实现表单控件和数据的双向绑定
v-html:更新元素的innerHTML
<p v-html="msg"></p>
var vm = new Vue({
el : "#app",
data : {
msg : "<h1>这是一个h1元素内容</h1>"
}
});
v-show与v-if:条件渲染,注意二者区别
v-on:click:可以简写为@click,@绑定一个事件。如果事件触发了,就可以指定事件的处理函数
v-for:基于源数据多次渲染元素或模板
v-bind:当表达式的值改变时,将其产生的连带影响,响应式地作用于DOM语法
v-bind:title=”msg”简写: :title="msg"
v-clock解决页面闪烁问题
如果网速慢,而该标签内容是变量没有请求响应回来的时候,页面上先不显示该标签(vue给该标签加了css样式),当响应回来的时候改标签默认将css样式去除。此指令可以解决使用插值表达式页面闪烁问题
将该指令加在html标签中时,可以在该文件中加style属性为display:none,例子如下:
<div class="#app" v-cloak>
<p>{{name}}</p>
</div>
[v-cloak] {
display: none;
}
4:绑定class的数组用法
1.对象方法v-bind:class="{‘orange‘:isRipe, ‘green‘:isNotRipe}”
:class="tindex == index ? ‘aaa‘:‘bbb‘"
2.数组方法v-bind:class="[class1,class2]"
3.行内v-bind:style="{color:color,fontSize:fontSize+‘px‘}”
5:计算属性和methods
computed:{
fullName:{
//get有什么作用? 当有人读取实例(data)上的数据时候,get就会被调用,返回值可以是data上的值
//get什么时候调用? 1.初次读取时,2.所依赖的数据发生变化的时候
get(){ //fullName被读取时调用
return this.firstName
},
//set什么时候调用? 当fullName被修改时
set(value){ //fullName被修改时调用
//非必须写
console.log(‘修改fullName后的值‘,value)
}
}
//简写方式
fullNamejianxie(){
return this.firstName + ‘jianxie‘
}
}
1.定义:要用的属性存在,要通过已有的属性计算而来
2.计算属性的底层借助Object.defineProperty()方法提供gettter/setter
3.get函数什么时候调用?
(1).初次读取时
(2).所依赖的数据发生变化的时
4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便
5.备注:
(1).计算属性最终会出现在vm(实例)上,直接读取即可
(2).如果计算属性被修改,那必须写set去响应数据,且set中要引起计算时依赖的数据发送变化
6:computed(计算属性)和watch(监听)的对比
computed
computed是计算属性,也就是计算值,它更多用于计算值的场景
computed具有缓存性,computed的值在getter执行后是会缓存的,只有在它依赖的属性值改变之后,下一次获取computed的值时重新调用对应的getter来计算
computed适用于计算比较消耗性能的计算场景
watch
<template>
<div class="container">
<!-- 1.监听实例上的基础数据类型 -->
<button @click="isHot = !isHot">{{isHot}}</button>
<hr>
<!-- 2.监听引用类型的数据 -->
<button @click="obj.age++">{{obj.age}}</button>
<hr>
<!-- 3.修改对象类型的数据变成字符串 -->
<button @click="obj = ‘变成其他数据类型了‘">{{obj}}</button>
</div>
</template>
<script>
export default {
name: ‘index‘,
data() {
return {
isHot : true,
obj :{
name: ‘js‘,
age : 18
}
}
},
watch:{
isHot:{
immediate : true, //初始化的时候,让handler调用一下
handler(newValue,oldValue){ //handle什么时候调用? isHot发送变化时候
console.log(‘isHot被修改了‘,newValue,oldValue)
}
},
// ‘obj.age‘ :{
// handler(newValue,oldValue){
// console.log(‘age被修改了‘,newValue,oldValue)
// }
// //监视多级结构中某个属性的变化(不建议这么写,如果有很多个值呢?)
// },
// obj:{
// //这么写即使是age的值发送改变,也不会有任何操作。
// //因为此时监视的是 obj下的整个对象,除非对象变成其他类型的数据才会触发
// handler(newValue,oldValue){
// console.log(‘obj被修改了‘,newValue,oldValue)
// }
// },
obj:{
//深度监视
//不管是obj的数据类型变了,还是里面的值变了
deep : true,
handler(newValue,oldValue){
console.log(‘obj被修改了‘,newValue,oldValue)
}
},
// 简写方式 =>
// 什么时候的时候才能简写? 只需要用到handler的时候
isHot(newValue,oldValue){
console.log(‘isHot被修改了‘,newValue,oldValue)
}
}
}
</script>
<style lang="less">
.container{
width: 100VW;
height: 100VH;
}
</style>
1.watch可以监听异步任务,计算属性无法开启异步任务
<template>
<div class="container">
<button @click="obj = ‘变成其他数据类型了‘">{{ obj }}</button>
<div>
{{ ahhh }}
</div>
</div>
</template>
<script>
export default {
name: "index",
data() {
return {
ahhh: 1,
obj: {
name: "js",
age: 18,
},
};
},
watch: {
obj: {
deep: true,
handler(newValue, oldValue) {
console.log("obj被修改了", newValue, oldValue);
setTimeout(() => {
//这里注意要用箭头函数(this会一层一层往外找),不能使用普通函数(this执行windows)
//开启异步任务
console.log("this", this);
this.ahhh += 1;
}, 1000);
},
},
},
};
</script>
<style lang="less">
.container {
width: 100vw;
height: 100vh;
}
</style>
7:vue组件的scoped属性的作用
在style标签上添加scoped属性,以表示它的样式作用于当下的模块,很好的实现了样式私有化的目的;
但是也得慎用:样式不易(可)修改,而很多时候,我们是需要对公共组件的样式做微调的;
解决办法:
①:使用混合型的css样式:(混合使用全局跟本地的样式) <style> /* 全局样式 */ </style><style scoped> /* 本地样式 */ </style>
②:深度作用选择器(>>>)如果你希望 scoped 样式中的一个选择器能够作用得“更深”,例如影响子组件,你可以使用 >>> 操作符:
<style scoped>
.a >>> .b { /* ... */ } </style>
8:vue常用修饰符
修饰符分为:一般修饰符,事件修身符,按键、系统
①一般修饰符
.lazy:v-model 在每次 input 事件触发后将输入框的值与数据进行同步 。你可以添加 lazy 修饰符,从而转变为使用 change 事件进行同步
<input v-model.lazy="msg" >
.number
<input v-model.number="age" type="number">
.trim
1.如果要自动过滤用户输入的首尾空白字符 <input v-model.trim=‘trim‘>
② 事件修饰符
1. 阻止单击事件继续传播
<a v-on:click.stop="doThis"></a>
2.提交事件不再重载页面
<form v-on:submit.prevent="onSubmit"></form>
3.添加事件监听器时使用事件捕获模式(即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理)
<div v-on:click.capture="doThis">...</div>
4.只当在 event.target 是当前元素自身时触发处理函数(即事件不是从内部元素触发的)
<div v-on:click.self="doThat">...</div>
5.点击事件将只会触发一次
<a v-on:click.once="doThis"></a>
6.vue组件添加事件
<Tabber @click.native="doThis"></Tabbar>
7.修饰符可以串联
<a v-on:click.stop.prevent="doThat"></a>
③按键修饰符
全部的按键别名:
.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
.ctrl
.alt
.shift
.meta
<input v-on:keyup.enter="submit"> 或者 <input @keyup.enter="submit">
④系统修饰键 (可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器)
.ctrl
.alt
.shift
.meta
<input @keyup.alt.67="clear"> 或者 <div @click.ctrl="doSomething">Do something</div><!-- Ctrl + Click -->
9:v-on可以监听多个事件处理吗?(可以的)
一个元素绑定多个事件的两种写法,一个事件绑定多个函数的两种写法,修饰符的使用。
<a style="cursor:default" v-on=‘{click:DoSomething,mouseleave:MouseLeave}‘>doSomething</a>
在method方法里面分别写两个事件;
<button @click="a(),b()">点我ab</button>
10:vue事件中使用event对象
<template>
//不加括号的的时候,直接拿到event对象
<button @click="btn">点击</button>
</template>
btn(value){
console.log(‘value‘,value)
}
//现在的需求是点击事件直接传值过来,但是也要event对象
<template>
//加了括号
<button @click="btn($event,1,2)">点击</button>
</template>
btn(value,...a){
console.log(‘value‘,value)
console.log(‘a‘,a) // [1,2]
}
11:nextTick
- 语法:
this.$nextTick(回调函数)
- 作用:在下一次 DOM 更新结束后执行其指定的回调。
- 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。
**比如你想让一个dom元素显示**,然后下一步去获取这个元素的offsetWidth,最后你获取到的会是0。
openSubmenu() {
this.show = true //获取不到宽度
this.$nextTick(() =>
//这里才可以 let w = this.$refs.submenu.offsetWidth;
})
}
13:Vue组件间传递数据的方式
1.props配置项(父传子 or 子传父)
-
功能:让组件接收外部传过来的数据
-
传递数据:
<Demo name="xxx"/>
-
接收数据:
-
第一种方式(只接收):
props:[‘name‘]
-
第二种方式(限制类型):
props:{name:String}
-
第三种方式(限制类型、限制必要性、指定默认值):
props:{ name:{ type:String, //类型 required:true, //必要性 default:‘老王‘ //默认值 } }
备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。
-
2.组件的自定义事件(子传父)
-
一种组件间通信的方式,适用于:子组件 ===> 父组件
-
使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
-
绑定自定义事件:
-
第一种方式,在父组件中:
<Demo @dataarr="test"/>
或<Demo v-on:dataarr="test"/>
-
第二种方式,在父组件中:
<Demo ref="demo"/> ...... mounted(){ this.$refs.xxx.$on(‘dataarr‘,this.test) }
-
若想让自定义事件只能触发一次,可以使用
once
修饰符,或$once
方法。
-
-
触发自定义事件:
this.$emit(‘dataarr‘,数据)
-
解绑自定义事件
this.$off(‘dataarr‘)
-
组件上也可以绑定原生DOM事件,需要使用
native
修饰符。 -
注意:通过
this.$refs.xxx.$on(‘dataarr‘,回调)
绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!
3.全局事件总线(GlobalEventBus)
-
一种组件间通信的方式,适用于任意组件间通信。
-
安装全局事件总线:
new Vue({ ...... beforeCreate() { Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm }, ...... })
-
使用事件总线:
-
接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
methods(){ demo(data){......} } ...... mounted() { this.$bus.$on(‘xxxx‘,this.demo) }
-
提供数据:
this.$bus.$emit(‘xxxx‘,数据)
-
-
最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
4.消息订阅与发布(pubsub)
-
一种组件间通信的方式,适用于任意组件间通信。
-
使用步骤:
-
安装pubsub:
npm i pubsub-js
-
引入:
import pubsub from ‘pubsub-js‘
-
接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
methods(){ demo(data){......} } ...... mounted() { this.pid = pubsub.subscribe(‘xxx‘,this.demo) //订阅消息 }
-
提供数据:
pubsub.publish(‘xxx‘,数据)
-
最好在beforeDestroy钩子中,用
PubSub.unsubscribe(pid)
去取消订阅。
-
//父组件需要拿到子组件的数据和实例
父组件调用子组件的方法 :this.$refs.yeluosen.childMethod()
//子组件拿到父组件的实例 (可以处理父组件分数据和调用父组件的方法)
this.$parent
14:vue更新响应式的缺陷和$set的使用
<template>
<div class="container">
<ul>
<li v-for="(item, index) in person" :key="index">
{{ item }}
</li>
</ul>
<button @click="addsex">手动点击给对象添加属性</button>
<hr />
<ul>
<li v-for="(item, index) in hobby" :key="index">
{{ item }}
</li>
</ul>
<button @click="addhobby">手动点击给数组修改属性</button>
</div>
</template>
<script>
export default {
name: "index",
data() {
return {
person: {
name: "jack",
age: 18,
},
hobby: ["抽烟", "喝酒", "烫头"],
persons: [
{ name: "gsq", age: 18 ,status : false },
{ name: "gsq02", age: 19,status : false },
{ name: "gsq03", age: 20,status : false },
],
};
},
methods: {
addsex() {
// 对象不能这么添加
// this.person.sex = ‘男‘
//正确写法
this.$set(this.person, "sex", "男")
},
addhobby(){
// 必须使用响应式的数组方法可以更新/或者使用this.$set
// 哪些数组是相应式的?
// push,pop,shift,unshift,splice,sort,reverse
// this.hobby.splice(0,1,‘哈哈‘)
// 或者使用
this.$set(this.hobby,0,‘哈哈‘)
}
},
};
</script>
<style lang="less">
.container {
width: 100vw;
height: 100vh;
}
</style>
Vue监视数据的原理:
1. vue会监视data中所有层次的数据。
2. 如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value) 或
vm.$set(target,propertyName/index,value)
3. 如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1).调用原生对应的方法对数组进行更新。
(2).重新解析模板,进而更新页面。
4.在Vue修改数组中的某个元素一定要用如下方法:
1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
2.Vue.set() 或 vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!
15:Vue获取dom元素节点并操作元素的样式
$refs 获取的是组件对象
$el 获取的是dom节点
1.获取到元素节点(这里通过this.$refs获取元素节点)
this.$refs.xxxxx.$el
2.操作元素的样式
this.$refs.num1Underline.$el.style.transform = ‘scaleX(1)‘
这种方法需要注意的是$el 用于获取vue挂载的实例的dom对象,如果不添加$el则获取不到style的属性值,会打印 TypeError: Cannot read property ‘style‘ of undefined 错误,这个错误大家一定都很熟悉,是类型错误,没有访问到dom元素
16:forceUpdate
//数据更新后,组件没更新。使用这个方法强制刷新组件
this.$forceUpdate();
17:mixin(混入)
1. 功能:可以把多个组件共用的配置提取成一个混入对象
2. 使用方式:
第一步定义混合:
{
data(){....},
methods:{....}
....
}
第二步使用混入:
? 全局混入:```Vue.mixin(xxx)```
? 局部混入:```mixins:[‘xxx‘] ```
18:插件
1. 功能:用于增强Vue
2. 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。
3. 定义插件:
对象.install = function (Vue, options) {
// 1. 添加全局过滤器
Vue.filter(....)
// 2. 添加全局指令
Vue.directive(....)
// 3. 配置全局混入(合)
Vue.mixin(....)
// 4. 添加实例方法
Vue.prototype.$myMethod = function () {...}
Vue.prototype.$myProperty = xxxx
}
4. 使用插件:Vue.use()
19:插槽
-
作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件 。
-
分类:默认插槽、具名插槽、作用域插槽
-
使用方式:
-
默认插槽:
父组件中: <Category> <div>html结构1</div> </Category> 子组件中: <template> <div> <!-- 定义插槽 --> <slot>插槽默认内容...</slot> </div> </template>
-
具名插槽:
父组件中: <Category> <template slot="center"> <div>html结构1</div> </template> <template v-slot:footer> <div>html结构2</div> </template> </Category> 子组件中: <template> <div> <!-- 定义插槽 --> <slot name="center">插槽默认内容...</slot> <slot name="footer">插槽默认内容...</slot> </div> </template>
-
作用域插槽:
-
理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
-
具体编码:
父组件中: <Category> <template scope="scopeData"> <!-- 生成的是ul列表 --> <ul> <li v-for="g in scopeData.games" :key="g">{{g}}</li> </ul> </template> </Category> <Category> <template slot-scope="scopeData"> <!-- 生成的是h4标题 --> <h4 v-for="g in scopeData.games" :key="g">{{g}}</h4> </template> </Category> 子组件中: <template> <div> <slot :games="games"></slot> </div> </template> <script> export default { name:‘Category‘, props:[‘title‘], //数据在子组件自身 data() { return { games:[‘红色警戒‘,‘穿越火线‘,‘劲舞团‘,‘超级玛丽‘] } }, } </script>
-
-
二:Vue-Router
1.基本使用
-
安装vue-router,命令:
npm i vue-router
-
应用插件:
Vue.use(VueRouter)
-
编写router配置项:
//引入VueRouter import VueRouter from ‘vue-router‘ //引入Luyou 组件 import About from ‘../components/About‘ import Home from ‘../components/Home‘ //创建router实例对象,去管理一组一组的路由规则 const router = new VueRouter({ routes:[ { path:‘/about‘, component:About }, { path:‘/home‘, component:Home } ] }) //暴露router export default router
-
实现切换(active-class可配置高亮样式)
<router-link active-class="active" to="/about">About</router-link>
-
指定展示位置
<router-view></router-view>
2.几个注意点
- 路由组件通常存放在
pages
文件夹,一般组件通常存放在components
文件夹。 - 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
- 每个组件都有自己的
$route
属性,里面存储着自己的路由信息。 - 整个应用只有一个router,可以通过组件的
$router
属性获取到。
3.多级路由(多级路由)
-
配置路由规则,使用children配置项:
routes:[ { path:‘/about‘, component:About, }, { path:‘/home‘, component:Home, children:[ //通过children配置子级路由 { path:‘news‘, //此处一定不要写:/news component:News }, { path:‘message‘,//此处一定不要写:/message component:Message } ] } ]
-
跳转(要写完整路径):
<router-link to="/home/news">News</router-link>
4.路由的query参数
-
传递参数
<!-- 跳转并携带query参数,to的字符串写法 --> <router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link> <!-- 跳转并携带query参数,to的对象写法 --> <router-link :to="{ path:‘/home/message/detail‘, query:{ id:666, title:‘你好‘ } }" >跳转</router-link>
-
接收参数:
$route.query.id $route.query.title
5.命名路由
-
作用:可以简化路由的跳转。
-
如何使用
-
给路由命名:
{ path:‘/demo‘, component:Demo, children:[ { path:‘test‘, component:Test, children:[ { name:‘hello‘ //给路由命名 path:‘welcome‘, component:Hello, } ] } ] }
-
简化跳转:
<!--简化前,需要写完整的路径 --> <router-link to="/demo/test/welcome">跳转</router-link> <!--简化后,直接通过名字跳转 --> <router-link :to="{name:‘hello‘}">跳转</router-link> <!--简化写法配合传递参数 --> <router-link :to="{ name:‘hello‘, query:{ id:666, title:‘你好‘ } }" >跳转</router-link>
-
6.路由的params参数
-
配置路由,声明接收params参数
{ path:‘/home‘, component:Home, children:[ { path:‘news‘, component:News }, { component:Message, children:[ { name:‘xiangqing‘, path:‘detail/:id/:title‘, //使用占位符声明接收params参数 component:Detail } ] } ] }
-
传递参数
<!-- 跳转并携带params参数,to的字符串写法 --> <router-link :to="/home/message/detail/666/你好">跳转</router-link> <!-- 跳转并携带params参数,to的对象写法 --> <router-link :to="{ name:‘xiangqing‘, params:{ id:666, title:‘你好‘ } }" >跳转</router-link>
特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
-
接收参数:
$route.params.id $route.params.title
7.路由的props配置
? 作用:让路由组件更方便的收到参数
{
name:‘xiangqing‘,
path:‘detail/:id‘,
component:Detail,
//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
// props:{a:900}
//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
// props:true
//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
props(route){
return {
id:route.query.id,
title:route.query.title
}
}
}
8.<router-link>
的replace属性
- 作用:控制路由跳转时操作浏览器历史记录的模式
- 浏览器的历史记录有两种写入方式:分别为
push
和replace
,push
是追加历史记录,replace
是替换当前记录。路由跳转时候默认为push
- 如何开启
replace
模式:<router-link replace .......>News</router-link>
9.编程式路由导航
-
作用:不借助
<router-link>
实现路由跳转,让路由跳转更加灵活 -
具体编码:
//$router的两个API this.$router.push({ name:‘xiangqing‘, params:{ id:xxx, title:xxx } }) this.$router.replace({ name:‘xiangqing‘, params:{ id:xxx, title:xxx } }) this.$router.forward() //前进 this.$router.back() //后退 this.$router.go() //可前进也可后退
10.缓存路由组件
-
作用:让不展示的路由组件保持挂载,不被销毁。
-
具体编码:
<keep-alive include="News"> <router-view></router-view> </keep-alive>
11.两个新的生命周期钩子
-
作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
-
具体名字:
-
activated
路由组件被激活时触发。 -
deactivated
路由组件失活时触发。
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
<keep-alive> <component> <!-- 该组件将被缓存! --> </component> </keep-alive> 如果只想 router-view 里面某个组件被缓存 export default [ { path: ‘/‘, name: ‘home‘, component: Home, meta: { keepAlive: true // 需要被缓存 } }, { path: ‘/:id‘, name: ‘edit‘, component: Edit, meta: { keepAlive: false // 不需要被缓存 } } ] <keep-alive> <router-view v-if="$route.meta.keepAlive"> <!-- 这里是会被缓存的视图组件,比如 Home! --> </router-view> </keep-alive> <router-view v-if="!$route.meta.keepAlive"> <!-- 这里是不被缓存的视图组件,比如 Edit! --> </router-view>
-
12.路由守卫
-
作用:对路由进行权限控制
-
分类:全局守卫、独享守卫、组件内守卫
-
全局守卫:
//全局前置守卫:初始化时执行、每次路由切换前执行 router.beforeEach((to,from,next)=>{ console.log(‘beforeEach‘,to,from) if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 if(localStorage.getItem(‘school‘) === ‘atguigu‘){ //权限控制的具体规则 next() //放行 }else{ alert(‘暂无权限查看‘) // next({name:‘guanyu‘}) } }else{ next() //放行 } }) //全局后置守卫:初始化时执行、每次路由切换后执行 router.afterEach((to,from)=>{ console.log(‘afterEach‘,to,from) if(to.meta.title){ document.title = to.meta.title //修改网页的title }else{ document.title = ‘vue_test‘ } })
-
独享守卫:
beforeEnter(to,from,next){ console.log(‘beforeEnter‘,to,from) if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 if(localStorage.getItem(‘school‘) === ‘atguigu‘){ next() }else{ alert(‘暂无权限查看‘) // next({name:‘guanyu‘}) } }else{ next() } }
-
组件内守卫:
//进入守卫:通过路由规则,进入该组件时被调用 beforeRouteEnter (to, from, next) { }, //离开守卫:通过路由规则,离开该组件时被调用 beforeRouteLeave (to, from, next) { }
13.vue-router实现路由懒加载( 动态加载路由 )
import Vue from ‘vue‘
import VueRouter from ‘vue-router‘
Vue.use(VueRouter)
const routes = [
{
path: ‘/‘,
name: ‘Home‘,
component: () => import( ‘../views/Home.vue‘) //使用import的方式导入组件
},
{
path: ‘/about‘,
name: ‘About‘,
component: () => import( ‘../views/About.vue‘)
}
]
const router = new VueRouter({
mode: ‘history‘,
base: process.env.BASE_URL,
routes
})
export default router
14.vue-router如何响应 路由参数 的变化?
原来的组件实例会被复用。这也意味着组件的生命周期钩子不会再被调用。你可以简单地 watch (监测变化) $route 对象:
const User = {
template: ‘...‘,
watch: {
‘$route‘ (to, from) {
// 对路由变化作出响应...
}
}
}
15.$route和$router的区别是什么
$route为当前router跳转当前路由组件里可获取name、path、query、params等
$router为VueRouter实例,想要导航到不同URL,则使用$router.push方法
16.hash和history的区别
-
对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
-
hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
-
hash模式:
- 地址中永远带着#号,不美观 。
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
- 兼容性较好。
-
history模式:
- 地址干净,美观 。
- 兼容性和hash模式相比略差。
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
三:Vuex
1.概念
? 在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
2.何时使用?
? 多个组件需要共享数据时,音乐播放、登录状态、加入购物车
3.搭建vuex环境
-
创建文件:
src/store/index.js
//引入Vue核心库 import Vue from ‘vue‘ //引入Vuex import Vuex from ‘vuex‘ //应用Vuex插件 Vue.use(Vuex) //准备actions对象——响应组件中用户的动作 const actions = {} //准备mutations对象——修改state中的数据 const mutations = {} //准备state对象——保存具体的数据 const state = {} //创建并暴露store export default new Vuex.Store({ actions, mutations, state })
-
在
main.js
中创建vm时传入store
配置项...... //引入store import store from ‘./store‘ ...... //创建vm new Vue({ el:‘#app‘, render: h => h(App), store })
4.基本使用
-
初始化数据、配置
actions
、配置mutations
,操作文件store.js
//引入Vue核心库 import Vue from ‘vue‘ //引入Vuex import Vuex from ‘vuex‘ //引用Vuex Vue.use(Vuex) const actions = { //响应组件中加的动作 jia(context,value){ // console.log(‘actions中的jia被调用了‘,miniStore,value) context.commit(‘JIA‘,value) }, } const mutations = { //执行加 JIA(state,value){ // console.log(‘mutations中的JIA被调用了‘,state,value) state.sum += value } } //初始化数据 const state = { sum:0 } //创建并暴露store export default new Vuex.Store({ actions, mutations, state, })
-
组件中读取vuex中的数据:
$store.state.sum
-
组件中修改vuex中的数据:
$store.dispatch(‘action中的方法名‘,数据)
或$store.commit(‘mutations中的方法名‘,数据)
备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写
dispatch
,直接编写commit
5.getters的使用
-
概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。
-
在
store.js
中追加getters
配置...... const getters = { bigSum(state){ return state.sum * 10 } } //创建并暴露store export default new Vuex.Store({ ...... getters })
-
组件中读取数据:
$store.getters.bigSum
6.四个map方法的使用
-
mapState方法:用于帮助我们映射
state
中的数据为计算属性computed: { //借助mapState生成计算属性:sum、school、subject(对象写法) ...mapState({sum:‘sum‘,school:‘school‘,subject:‘subject‘}), //借助mapState生成计算属性:sum、school、subject(数组写法) ...mapState([‘sum‘,‘school‘,‘subject‘]), },
-
mapGetters方法:用于帮助我们映射
getters
中的数据为计算属性computed: { //借助mapGetters生成计算属性:bigSum(对象写法) ...mapGetters({bigSum:‘bigSum‘}), //借助mapGetters生成计算属性:bigSum(数组写法) ...mapGetters([‘bigSum‘]) },
-
mapActions方法:用于帮助我们生成与
actions
对话的方法,即:包含$store.dispatch(xxx)
的函数methods:{ //靠mapActions生成:incrementOdd、incrementWait(对象形式) ...mapActions({incrementOdd:‘jiaOdd‘,incrementWait:‘jiaWait‘}) //靠mapActions生成:incrementOdd、incrementWait(数组形式) ...mapActions([‘jiaOdd‘,‘jiaWait‘]) }
-
mapMutations方法:用于帮助我们生成与
mutations
对话的方法,即:包含$store.commit(xxx)
的函数methods:{ //靠mapActions生成:increment、decrement(对象形式) ...mapMutations({increment:‘JIA‘,decrement:‘JIAN‘}), //靠mapMutations生成:JIA、JIAN(对象形式) ...mapMutations([‘JIA‘,‘JIAN‘]), }
备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。
7.模块化+命名空间
-
目的:让代码更好维护,让多种数据分类更加明确。
-
修改
store.js
const countAbout = { namespaced:true,//开启命名空间 state:{x:1}, mutations: { ... }, actions: { ... }, getters: { bigSum(state){ return state.sum * 10 } } } const personAbout = { namespaced:true,//开启命名空间 state:{ ... }, mutations: { ... }, actions: { ... } } const store = new Vuex.Store({ modules: { countAbout, personAbout } })
-
开启命名空间后,组件中读取state数据:
//方式一:自己直接读取 this.$store.state.personAbout.list //方式二:借助mapState读取: ...mapState(‘countAbout‘,[‘sum‘,‘school‘,‘subject‘]),
-
开启命名空间后,组件中读取getters数据:
//方式一:自己直接读取 this.$store.getters[‘personAbout/firstPersonName‘] //方式二:借助mapGetters读取: ...mapGetters(‘countAbout‘,[‘bigSum‘])
-
开启命名空间后,组件中调用dispatch
//方式一:自己直接dispatch this.$store.dispatch(‘personAbout/addPersonWang‘,person) //方式二:借助mapActions: ...mapActions(‘countAbout‘,{incrementOdd:‘jiaOdd‘,incrementWait:‘jiaWait‘})
-
开启命名空间后,组件中调用commit
//方式一:自己直接commit this.$store.commit(‘personAbout/ADD_PERSON‘,person) //方式二:借助mapMutations: ...mapMutations(‘countAbout‘,{increment:‘JIA‘,decrement:‘JIAN‘}),
2:Vuex state数据的双向绑定
// 在从组件的computed中
computed: {
user: {
get() {
return this.$store.state.user
},
set(v) {
// 使用vuex中的mutations中定义好的方法来改变
this.$store.commit(‘USER‘, v)
}
}<br>}<br>// 在组件中就可以使用
<input v-modle="user" />
四:Vue中的开发经验
1:搭建Vue脚手架(vue-cli)并创建一个项目
1.安装node.js环境
[官网下载地址](https://nodejs.org/en/download/)
一路安装完成后 打开cmd
输入 node -v
输入 npm -v
如果能看到node和npm的版本号了,说明已经安装成功
2.安装vue-cli
有npm和cnpm两种方式,网上都说cnpm好些,所以我也用的cnpm安装,
首先利用淘宝镜像安装cnpm
npm install cnpm -g --registry=https://registry.npm.taobao.org
安装完成
输入 cnpm -v
3.然后全局安装 vue-cli
输入指令 cnpm install -g vue-cli
这个命令只需要运行一次就可以了。安装上之后,以后就不用安装了。
查看vue版本号
vue -V //注:V是大写字母V
如果提示“无法识别 ‘vue’ ” ,有可能是 npm 版本过低,需要手动更新npm的版本号,这也是上面说的为什么要保证npm版本号高的原因,npm的版本可以手动升级更新,没记错的话应该是
npm install -g npm
4.使用vue-cli来创建一个基于 webpack 模板的新项目
创建
cmd利用cd指令进入到保存项目的文件夹下,然后输入命令
输入 vue init webpack
5.安装项目所需要的依赖
进入新建的项目文件夹下,就是上头有一些文件的里面,输入命令
输入 指令 cnpm install
6.运行项目
根据你package.json里的配置里的指令
一般情况下都是 npm run dev
7.打包资源上线
根据你package.json里的配置里的指令
一般情况下都是 npm run build
8.具体创建项目 博客地址
https://www.cnblogs.com/coober/p/10875647.html
2:关于不同版本的Vue
- vue.js与vue.runtime.xxx.js的区别:
- vue.js是完整版的Vue,包含:核心功能 + 模板解析器。
- vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。
- 因为vue.runtime.xxx.js没有模板解析器,所以不能使用template这个配置项,需要使用render函数接收到的createElement函数去指定具体内容。
3:vue.config.js配置文件
- 使用vue inspect > output.js可以查看到Vue脚手架的默认配置。
- 使用vue.config.js可以对脚手架进行个性化定制,详情见:https://cli.vuejs.org/zh
2:vue中如何编写可复用的组件?
①创建组件页面eg Toast.vue;
②用Vue.extend()扩展一个组件构造器,再通过实例化组件构造器,就可创造出可复用的组件
③将toast组件挂载到新创建的div上;
④把toast组件的dom添加到body里;
⑤修改优化达到动态控制页面显示文字跟显示时间;
import Vue from ‘vue‘;
import Toast from ‘@/components/Toast‘; //引入组件
let ToastConstructor = Vue.extend(Toast) // 返回一个“扩展实例构造器”
let myToast = (text,duration)=>{
let toastDom = new ToastConstructor({
el:document.createElement(‘div‘) //将toast组件挂载到新创建的div上
})
document.body.appendChild( toastDom.$el ) //把toast组件的dom添加到body里
toastDom.text = text;
toastDom.duration = duration;
// 在指定 duration 之后让 toast消失
setTimeout(()=>{
toastDom.isShow = false;
}, toastDom.duration);
}
export default myToast;
点击查看 https://blog.csdn.net/qq_38563845/article/details/77524934
3:vue父组件向子组件传对象,不实时更新解决
思路1:就是让利用v-if的重新渲染机制
1.首先考虑的就是手动刷了,给组件加个v-if=”someShow“;
// 这是组件上写法 :<my-component v-if="someShow"></my-component>
// 下边写在父组件的methods里
refesh:function(){
this.someShow=false;
var _this=this;
this.$nextTick(function(){
_this.someShow = true;
})
}
// $nextTick
// $nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick,则可以在回调中获取更新后的 DOM 这样重新渲染就会是最新数据了
思路2:利用watch监听
在子组件中监听你要的数据,当然别监听对象,监听了不一定好使。亲测不好使,测过好使的,可以给我留言。
data:function(){
return {
title:"",
content:"",
btn:""
}
},
methods:{
changeTitle:function(){
this.title=this.listTitle;
// 这里的每次变化了就复制给组件上的变量,视图也就更改了
},
changeList:function(){
this.content=this.listList;
},
changeBtn:function(){
this.btn=this.listBtn;
}
},
watch:{
listTitle:"changeTitle",
listList:"changeList",
listBtn:"changeBtn"
// 冒号前边这个就是从父组件传过来的,后边的就是变化了的监听函数
}
4:is的用法(用于动态组件且基于 DOM 内模板的限制来工作。)
is用来动态切换组件,DOM模板解析
<table> <tr is="my-row"></tr> </table>
5:vue脚手架配置代理
方法一
? 在vue.config.js中添加如下配置:
devServer:{
proxy:"http://localhost:5000"
}
说明:
- 优点:配置简单,请求资源时直接发给前端(8080)即可。
- 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
- 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)
方法二
? 编写vue.config.js配置具体代理规则:
module.exports = {
devServer: {
proxy: {
‘/api1‘: {// 匹配所有以 ‘/api1‘开头的请求路径
target: ‘http://localhost:5000‘,// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {‘^/api1‘: ‘‘}
},
‘/api2‘: {// 匹配所有以 ‘/api2‘开头的请求路径
target: ‘http://localhost:5001‘,// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {‘^/api2‘: ‘‘}
}
}
}
}
/*
changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
changeOrigin默认值为true
*/
说明:
- 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
- 缺点:配置略微繁琐,请求资源时必须加前缀。
五:Vue源码原理
1:vue.js2.x的两个核心是什么(数据驱动、组件系统。)
数据驱动:Object.defineProperty和存储器属性: getter和setter(所以只兼容IE9及以上版本),可称为基于依赖收集的观测机制,核心是VM,即ViewModel,保证数据和视图的一致性。
let number = 18
let person = {
name : ‘jack‘,
sex : ‘男‘
}
Object.defineProperty(person,‘age‘,{
// value : 18,
// enumerable : true, //控制熟悉是否可以枚举,默认值是false
// writable : true, //控制熟悉是否可以被修改,默认值是false
// configurable : true, //控制属性是否可以被删除,默认值是false
//当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
get(){
console.log(‘有人读取了age‘)
return number
},
//当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
set(value){
console.log(`有人修改了age,值为:${value}`)
number = value
}
})
组件系统:
2:VUE数据代理的原理
通过一个对象代理对另一个对象中的属性的操作(读写)
let obj = {x:100}
let obj2 = {y:200}
Object.defineProperty(obj2,‘x‘,{
get(){
return obj.x
},
set(){
obj.x = value
}
})
1.Vue中的数据代理:
通过vm对象代理data对象中的数据(读/写)
2.Vue中数据代理的好处:
更加方便的操作data中的数据
3.基本原理
通过Object.defineProperty()把data对象中所有属性添加到vm上
为每一个添加到vm上的属性,都指定一个getter/setter
在getter/setter内部去操作(读/写)data中对应的属性
3:Vue 组件中 data 为什么必须是函数
vue组件中data值不能为对象,因为对象是引用类型,组件可能会被多个实例同时引用。
如果data值为对象,将导致多个实例共享一个对象,其中一个组件改变data属性值,其它实例也会受到影响。
4:Vue双向绑定的原理
5:Vue的diff算法(列表渲染key的作用和原理)
1.虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当状态中的数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】
随后,Vue进行【新的虚拟DOM】与【旧的虚拟DOM】的差异比较,比较规则如下
2.对比规则:
(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
1.若虚拟DOM中的内容没变,直接使用之前的真实DOM!
2.若虚拟DOM中的内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
(2).旧虚拟DOM中未找到与新虚拟DOM相同的key
1.创建新的真实DOM,随后渲染到页面
3.用index作为key可能会引发的问题:
1.若对数据进行,逆序添加,逆序删除等破坏顺寻操作:
会产生没有必要的真实DOM更新 => 界面效果没问题,但是效率低
2.如果结构中还有包含输入类的DOM:
会产生错误DOM更新 => 界面有问题
4.开发中如何选择key?:
1.最好使用每条数据的唯一标识作为key,比如id,手机号,身份证号,学号等唯一值
2.如果不存在对数据的逆序添加,逆序删除等破坏性顺序操作,仅用于渲染列表展示,使用index作为key是没问题的
6:Vue是怎么检测数据的改变的原理
let data = {
name :‘jack‘,
age : 18
}
//创建一个监视的实例对象,用于监视data中属性的变化
const obs = new Observer(data)
//准备一个vm实例对象
let vm = {}
vm._data = data = obs
function Observer(obj){
//汇总对象中所有属性形成的数组
const keys = Object.keys(obj)
//遍历
keys.forEach((k)=>{
Object.defineProperty(this,k,{
get(){
return obj[k]
},
set(val){
console.log(`${k}被改了,我要去解析模板了,生成虚拟DOM`)
obj[k] = val
}
})
})
}
//自己实现的基本的原理,只是对第一层的基本数据类型进行检测。无法检测到里面的
Vue的底层对data中的对象数据检测进行了递归,所以都能检测到,
数组里的对象数据没有检测
六:对Vue的理解
1:谈谈对MVVM的理解
M-model,model代表数据模型,也可以在model中定义数据修改和操作的业务逻辑
V-view,view代表UI组件,它负责将数据模型转化为UI展现出来
VM-viewmodel,viewmodel监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步view和model的对象,连接model和view
2:vue是渐进式的框架的理解:(主张最少,没有多做职责之外的事)
Vue的核心的功能,是一个视图模板引擎,但这不是说Vue就不能成为一个框架。如下图所示,这里包含了Vue的所有部件,在声明式渲染(视图模板引擎)的基础上,我们可以通过添加组件系统、客户端路由、大规模状态管理来构建一个完整的框架。更重要的是,这些功能相互独立,你可以在核心功能的基础上任意选用其他的部件,不一定要全部整合在一起。可以看到,所说的“渐进式”,其实就是Vue的使用方式,同时也体现了Vue的设计的理念
在我看来,渐进式代表的含义是:主张最少。视图模板引擎
每个框架都不可避免会有自己的一些特点,从而会对使用者有一定的要求,这些要求就是主张,主张有强有弱,它的强势程度会影响在业务开发中的使用方式。
比如说,Angular,它两个版本都是强主张的,如果你用它,必须接受以下东西:
必须使用它的模块机制- 必须使用它的依赖注入- 必须使用它的特殊形式定义组件(这一点每个视图框架都有,难以避免)
所以Angular是带有比较强的排它性的,如果你的应用不是从头开始,而是要不断考虑是否跟其他东西集成,这些主张会带来一些困扰。
Vue可能有些方面是不如React,不如Angular,但它是渐进的,没有强主张,你可以在原有大系统的上面,把一两个组件改用它实现,当jQuery用;也可以整个用它全家桶开发,当Angular用;还可以用它的视图,搭配你自己设计的整个下层用。也可以函数式,都可以,它只是个轻量视图而已,只做了自己该做的事,没有做不该做的事,仅此而已。
渐进式的含义,我的理解是:没有多做职责之外的事。
3:vue等单页面应用及其优缺点
缺点:
不支持低版本的浏览器,最低只支持到IE9;
不利于SEO的优化(如果要支持SEO,建议通过服务端来进行渲染组件);
第一次加载首页耗时相对长一些;
不可以使用浏览器的导航按钮需要自行实现前进、后退。
优点:
无刷新体验,提升了用户体验;
前端开发不再以页面为单位,更多地采用组件化的思想,代码结构和组织方式更加规范化,便于修改和调整;
API 共享,同一套后端程序代码不用修改就可以用于Web界面、手机、平板等多种客户端
用户体验好、快,内容的改变不需要重新加载整个页面。
七:Vue3扩展
1.VUE3简介
- 2020年9月18日,Vue.js发布3.0版本,代号 One Piece(海贼王)
2.Vue3带来了什么
1.性能的提升
1.打包大小减少41%
2.初次渲染快55%,更新渲染快33%
3.内存减少54%
...
2.源码的升级
使用Proxy代替defineProperty实现响应式
重写虚拟DOM的实现和Tree-Shaking
...
3.拥抱TypeScript
Vue3可以更好的支持TypeScript
4.新的特性
1.Composition API(组合API)
steup配置
ref与reactive
watch与watchEffect
provide与inject
...
2.新的内置组件
Fragment
Teleport
Suspense
3.其他改变
新的生命周期钩子
data选项应始终被声明为一个函数
移除keyCode支持作为v-on的修饰符
...
3.创建Vue3.0工程
1.使用vue-cli创建
## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version 或者 vue -V
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create vue-text /或者使用 node自带的(如果升级不了@vue/cli) npx @vue/cli create my-app
## 启动
cd vue-text
npm run serve
2.使用vite创建
## 什么是vite?
新一代前端构建工具
## 优势
1.开发环境中,无需打包操作,可快速的冷启动
2.轻量快速的热重载(HMR)
3.真正的按需编译,不再等待整个应用编译完成
## 创建工程
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev
4.常用Composition API
1.拉开序幕额setup
1.理解:vue3.0中的一个新的配置项。值为一个函数
2.setups是所有Composition API(组合api) “表演的舞台”
3.组件中所有用到的:数据,方法等等。均要配置在setup中
4.setup函数的两种返回值:
1.若返回一个对象,则对象中的属性,方法,在模板中直接使用
2.若返回画一个渲染函数,则可以自定义渲染内容(了解即可)
5.注意点
1.尽量不要与Vue2.x配置混用
2.但在setup中不能访问到Vue2.x配置(data,methsds,computed...)
3.如果重名,setup优先
2.ref函数
1.作用:定义一个响应式的数据
2.语法 : const xxx = ref(initValue)
1.创建一个响应式的数据的引用对象
2.js操作数据
3.模板中读取数据:不需要 .value 直接 <div>{{xxx}}<div/>
3.备注:
1.接收数据可以是基本类型,也可以是对象类型
2.基本类型的数据:响应式依然是靠Object.defineProperty() 的get和set
3.对象类型的数据: 内部求助了Vue3.0中的一个新的函数 - reactive
3.reactive
1.作用:定义一个对象类型的响应式数据(基本类型别用它,用ref函数)
2.语法 const 代理对象 = reactive(被代理对象)接收一个对象或者数组,返回一个代理器对象(proxy对象)
3.reactive定义的响应数据是深层次的
4.内部基于ES6的proxy,通过代理对象对原数据内部数据都是响应式的