Vue3
Vue3.0的优势
- 性能比Vue2.x快1.2~2倍
- 按需编译,体积比Vue2.x更小
- 组合API(类似React Hooks)
- 更好的TS支持
- 暴露自定义渲染API
Vue3.0如何变快得?
- diff方法优化
- Vue2中得虚拟dom是进行全量得对比
- Vue3新增了静态标记
- 在与上次虚拟节点进行对比时候,只对比带有patch flag得节点,并且可以通过flag(例如:1就代表动态文本节点)得信息得知当前节点要对比得具体内容
- hoistStatic 静态提升
- Vue2中无论元素是否参与更新,每次都会重新创建
- Vue3中对于不参与更新得元素,只会被创建一次,之后会在每次渲染得时候被不停得复用
- cacheHandlers 事件侦听器缓存
- 默认情况下onClick会被视为动态绑定,所以每次都会去追踪它得变化,但是因为是同一个函数,所以没有追踪变化(也是通过flag),直接缓存起来复用即可
- SSR渲染
- 当有大量静态得内容得时候,这些内容会被当做纯字符串推进一个buffer里面,即使存在动态得绑定,会通过模板插值嵌入进去,这样会比通过虚拟dom
渲染得快上很多很多 - 静态内容大到一定量级时候,会用_createStaticVNode方法在客户端去生成一个static node,这些静态node,会被直接innerHTML,就不需要创建对象,然后根据对象渲染了
- 当有大量静态得内容得时候,这些内容会被当做纯字符串推进一个buffer里面,即使存在动态得绑定,会通过模板插值嵌入进去,这样会比通过虚拟dom
Vue3.0快速上手
当然也有脚手架和webpack的方式,但是此时只使用这个 Vite打包使用的是rollup
-
什么是Vite?
Vite是Vue作者开发的一款意图取代webpack的工具,其实现原理是利用ES6的import会发送请求去加载文件的特性,拦截这些请求,做一些预编译,省去webpack冗长的打包时间
-
安装Vite
npm i -g create-vite-app
-
创建项目
create-vite-app projectName
-
安装依赖然后运行
脚手架形式
- npm install -g @vue/cli
- vue create hello-vue3
知识点补充
<template>
<div>
<!-- 注意此处,本来bind里面绑定的应该是变量,但是此时传递固定值则需要加'' -->
<a v-bind:[myHref]="'https://www.baidu.com'">跳转百度</a>
<!-- 对象也可遍历 -->
<li v-for="(value, name, index) in myObject" :key="index">
{{ name }}--{{ value }}---{{ index }}
</li>
<button @click="one($event), two($event)">多事件处理</button>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
infos: "嘿嘿",
infos1: "哈哈",
myHref: "href",
// myHref:'title',
myObject: {
title: "ahdhasa",
name: "zq",
},
};
},
methods: {
one(e) {
console.log(e);
},
two(e) {
console.log(e);
},
},
};
</script>
自定义事件
<template>
<div>
<!-- 自定义事件 -->
<Child @send="getChild" />
</div>
</template>
<script>
import Child from "./components/Child";
export default {
name: "App",
components: {
Child
},
methods: {
getChild(data) {
console.log("子组件触发了该方法", data);
}
},
};
</script>
<template>
<div>
<p>Child</p>
<button @click="test">执行父组件的自定义事件</button>
</div>
</template>
<script>
export default {
//自定义事件还可以添加上校验,但是一般不需要
emits:{
//这个send需要和自定义事件名称保持一致
send:({msg})=>{
//注意:虽然验证,但是还是会把数据传递到父组件的
if (msg.length>10) {
return true
} else {
console.log(111,"数据长度不足");
return false
}
}
},
//建议定义的所有发出的事件都记录如下,虽然不记录也可以
// emits:["xxx-xxx"],
data(){
return{
msg:'子组件数据'
}
},
methods:{
test(){
//send就是组件上面的自定义函数名
// this.$emit('send','子组件数据')
this.$emit('send',{msg:this.msg})
}
}
}
</script>
自定义输入框实现双向数据绑定
- 默认情况下,组件上的v-model使用modelvalue作为prop和update:modelValue作为事件。
- 可以通过v-model传递参数来修改这些名称
<template>
<div>
<!-- 自定义输入框组件,实现双向数据绑定,infos是自定义属性名称,不定的 -->
<MyComponent v-model:infos="infos" v-model:infos1="infos1" />
</div>
</template>
<script>
import MyComponent from "./components/MyComponent";
export default {
name: "App",
components: {
MyComponent
},
data() {
return {
infos: "嘿嘿",
infos1: "哈哈",
};
}
};
</script>
<template>
<!-- update是固定的,infos的app.vue中的关键字是自定义的,对应起来就行 -->
<input
type="text"
:value="infos"
@input="$emit('update:infos', $event.target.value)"
/>
<input
type="text"
:value="infos1"
@input="$emit('update:infos1', $event.target.value)"
/>
</template>
<script>
export default {
props: ["infos","infos1"],
};
</script>
非prop的attribute继承
- 一个非prop的attribute是指传向一个组件,但是该组件并没有相应的props或emits定义的attribute。
- 常见的示例包含class style id属性
<template>
<div>
<!-- 非prop的attribute继承 -->
<Date class="datecss" />
</div>
</template>
- Date.vue
<template>
<!-- 其实可以发现,此处div被添加了datecss的样式 -->
<div>
hello world
</div>
</template>
自定义attribute继承
- 如果你不希望组件的根元素继承attribute,可以在组件的选项中设置inheritAttrs:false;
- 例如:禁用attribute继承的常见情况是需要将attribute应用于根节点之外的其他元素
- 通过设置inheritAttrs:false,可以访问组件的$attr的property,该property包括组件props和emits property中未包含的所有属性(例如:class style v-on监听器等
<template>
<div>
<!-- 自定义attribute继承 -->
<DatePicker data-time="2020-11-11" />
</div>
</template>
- DatePicker.vue
<template>
<!-- 而且vue3允许多个根节点,即使默认继承如果出现多个根节点也会报警告,所以多个根节点也需要指定哪个继承,例如 v-bind="$attrs" -->
<!-- 本来data数据属于attribute,默认是给了当面的最外层div,可以通过查看dom确定 -->
<div class="datapicker">
<!-- 但是此时需要让input使用,而不是上层的div -->
<!-- 通过v-bind="$attrs"实现,前提是inhertAttrs:false,//禁用默认继承 -->
<input type="date" v-bind="$attrs" />
</div>
</template>
<script>
export default {
inhertAttrs:false,//禁用默认继承
};
</script>
自定义dialog内部关闭dialog
<template>
<div>
<button @click="isVisible=true">弹出弹窗</button>
<Dialog :visible="isVisible" @close-modal="isVisible=false"/>
<Home3/>
</div>
</template>
<script>
import Dialog from "./components/Dialog";
export default {
name: "App",
components: {
Dialog
},
data() {
return {
isVisible:false
};
}
};
</script>
- Dialog.vue
<template>
<teleport to="body">
<div v-if="visible">
我是弹窗,样式不计,只说明功能
<button @click="$emit('close-modal')">关闭</button>
</div>
</teleport>
</template>
<script>
export default {
props: ["visible"],
};
</script>
teleport
- Vue3中的组件模板属于该组件,有时候想把模板的内容移动到当前组件之外的dom中,这个时候就可以使用Teleport了
- 表示把teleport内包含的内容显示到body中
<teleport to="body">
内容
</teleport>
同理可以 to="#app"这种形式
例如:弹窗组件,点击弹出,内部一定通过css样式确定该组件的位置,但是弹窗组件所有位置,可能外部有定位样式,影响了本身弹窗的样式,
导致显示混乱,这时候就可以通过teleport然后指定显示插入的节点位置,
例如放到body节点,则基本上无影响了。虽然代码形式上可能弹窗再某个组件内
部,但是实际显示时候却是再body根节点上面
vue3生命周期
- beforeCreate:实例刚被创建
- created:实例已经创建完成
- beforeMount:模板编译之前
- mounted:模板编译完成
- beforeUpdate:数据更新之前
- updated:数据更新完毕
- actived: keep-alive缓存的组件激活时调用
- deactived: keep-alive缓存的组件停用时调用
- beforeUnmount:对应2.x的beforeDestory,页面销毁的时候要保存一些数据,就在此处
- unmounted:2.x的destoryed 实例销毁完成
import {onMounted,oUnmounted} from 'vue';
set(){
onMounted(()=>{
console.log('onMounted');
});
}
Compositon API介绍
reactive
作用:创建响应式对象,非包装对象,可以认识是模板中的状态
- template可以放兄弟节点
- reactive类似useState,如果参数是字符串,数字,会报警告,value cannot be made reactive,所以应该设置对象,这样可以数据驱动页面
<div>
{{countobj.count}}-<button @click="add()">Add</button>
</div>
setup(){
const countobj =reactive({count:0});
const add=() => {
countobj.count++;
}
return {countobj,add}
}
<template>
<div>child-hooks-{{ mytitle }}-{{ mytext }}</div>
<div>
navbar-<button @click="handleClick">navbar-click</button>
</div>
</template>
<script>
import { ref } from "vue";
export default {
props: ["mytitle"],
//setup内部没有this,可以这样获取prosp;
setup(props, { emit }) {
// console.log(props.mytitle)
const mytext = ref(props.mytitle + "11111111111111111");
const handleClick = () => {
emit('kerwinevent')
}
return {
mytext,
handleClick
};
},
};
</script>
- 下面是父级接受emit
<template>
<div>
<navbar @kerwinevent="handleChange"/>
<sidebar v-if="state.isShow"/>
</div>
</template>
ref
作用:创建一个包装式对象,含有一个响应式属性value。它和reactive的差别,就说前者没有包装属性value
const count =ref(0); 可以接收普通数据类型,count.value++
<div>
{{count}}-<button @click="add()">Add</button>
</div>
setup(){
const count =ref(0);
const add=() => {
count.count++;
}
return {count,add}
}
ref嵌套在reactive中
<template>
<div class="home">
home-{{count}}--{{state.count}}
<button @click="add">click</button>
</div>
</template>
<script>
import {reactive,ref} from 'vue';
export default{
name:'Home',
setup(){
const count = ref(0);
const state = reactive({count})
const add=() => {
state.count++
//state.count跟ref count都会更新;
}
return{ count,add,state}
}
}
</script>
toRefs
默认直接展开state,那么此时reactive数据变成普通数据,通过toRefs,可以把reactive里的每个属性,转化为ref对象,这样展开后,就会变成多个ref对象,以然具有响应式特性
<template>
<div class="home">
home-{{count}}
<button @click="add">click</button>
</div>
</template>
<script>
import {reactive,ref} from 'vue';
export default{
name:'Home',
setup(){
const state = reactive({count})
const add=() => {
state.count++
}
return{ add,...toRefs(state)}
}
}
</script>
ref访问dom或者组件
<input type="text" ref="myinput">
const myinput =ref(null);
console.log(myinput.value.value);
计算属性
set(){
const mytext = ref('');
const computedSum=computed(()=>mytext.value.substring(0,1).toUpperCase()+mytext.value.substring(1)+mytext.value.substring(1));
return {mytext,computedSum}
}
watch
监听器watch是一个方法,它包含两个参数
const reactivedata=reactive({count:1});
const text=ref("");
watch(()=>reactivedata.count,val=>{
});
watch(text,val=>{
})
//注意:reactive和ref的监听有细微区别
第一个参数是监听的值,count.value表示当count.value发生变化就会触发监听器的回调函数,即第二个参数,底二个参数可以执行监听时候的回调
vue3组合api的使用
通俗来说,option API在中小型项目没问题,但是大型项目,建议使用composition API,方便逻辑复用
<template>
<div>
<!-- vue3组合api的使用 -->
<Home3/>
</div>
</template>
- Home3.vue
<template>
<div>------{{ title }}----------</div>
<button @click="getTitle">getTitle</button>
<button @click="getInfo">getInfo</button>
<button @click="setTitle">setTitle</button>
<p>我是分割线----------------</p>
{{ descrip }}--{{ auth }}
<p>{{ fullName }}</p>
<p>watchEffect功能演示</p>
<p>{{ data.num }}</p>
</template>
<script>
import { ref, reactive, toRefs, computed, watchEffect } from "vue";
export default {
setup() {
//定义响应式数据-如果不用这些定义,则只是普通数据,非响应式的(原始对象)
//ref:基本数据类型 reactive:对象
let title = ref("我是一个标题");
let info = reactive({ info: "infossss" });
let article = reactive({ descrip: "描述", auth: "作者" });
// title=readonly(title); //只读
let getTitle = () => {
console.log(title.value); //注意此处,是title.value
};
let setTitle = () => {
title.value = "修改的";
};
let getInfo = () => {
console.log(info.info);
};
const user = reactive({
fn: "1",
ln: "2",
});
//计算属性
const fullName = computed(() => {
return user.fn + "------" + user.ln;
});
//watchEffect功能
let data = reactive({
num: 1,
});
watchEffect(() => {
//和watch不同,不论改不改变监听得值,都会执行该函数,即最开始时候也会执行一次的,也算作更新
//而且watch必须监听data即大对象才能监听,但是watchEffect可以监听具体的data.num,
//也就是说watchEffect可以监听当前回调里面所有数据变化,即使只是打印一个log
console.log(`num=${data.num}`);
});
// watch(num1,(newValue,oldValue)=>{
// })
setInterval(() => {
data.num++;
}, 1000);
return {
title,
info,
getTitle,
getInfo,
setTitle,
data,
// ...article //三点运算符相当于把article内部对象解构到这里,但是不是响应式数据,解决方案toRefs
...toRefs(article),
fullName,
};
},
};
</script>
- watchEffect
在响应式的跟踪其依赖项时立即运行一个函数,并在更改依赖项时重新运行它.
- watch和watchEffect的区别
- 懒执行,也就是说仅在侦听的源变更时才执行回调
- 更明确哪些状态的改变会触发侦听器重新运行
- 访问侦听状态变化前后的值
Provider Inject
通常需要将数据从父组件传递到子组件时,使用props。但是,有一些深嵌套的组件,需要来自深嵌套子组件中父组件的某些内容;这种情况,相当不方便
可以使用provide和inject对父组件可以作为其子组件的依赖项提供程序,而不管组件层次结构有多深。
这个特性有两部分,父组件provide选项来提供数据,子组件有一个inject选项来开始使用这个数据
事件总线
- vue3.x从实例移除了off $once
- 但是$emit仍然是现有api的一部分,可是只能实现子组件触发父组件的方法
- 实现非父子组件通信,可以使用第三方
npm install --save mitt
- 新建model\event.js
import mitt from 'mitt'
const VueEvent=mitt();
export default VueEvent;
混入
- 混入和当前都有同一个属性,则会应用当前组件的属性
- 也可以全局配置mixin,这样就不需要再每个组件里面配置了
hooks对应的生命周期
1.pngsetup函数说明
setup函数,是在beforecreate钩子之前完成的,所以data中数据和methods中函数是无法使用的,setup中的this被vue修改成了underfined;
并且setup函数必须是同步的,不能是异步的(async)
- beforeCreate:组件刚刚被创建出来,组件的data和methods还没有初始化好
- created: 组件刚刚被创建出来,并且组件的data和methods已经初始化好了
- 什么是reactive?
- reactive是Vue3中提供的实现响应式数据的方法
- 而在Vue3中响应式数据是通过ES6的Proxy实现的
- 什么是ref?
- ref和reactive一样,也是用来实现响应式数据的方法
- 由于reactive必须传递一个对象,所以导致在开发中如果只想让某个变量实现响应式的时候会非常麻烦,所以提供了ref
- ref本质还是reactive,ref(xx)=>reactive({value:xx})
- ref注意点:Vue中使用ref的值不用通过value获取,但是js中使用的话必须使用value获取
- Vue是如果判断数据是ref还是reactive的?通过当前数据的__v_ref来判断的,如果是true就代表是ref数据,reactvie没有这个私有属性
- 可以通过isRef和isReactive来判断数据类型
<template>
<div>
<p>{{ count }}</p>
<button @click="fn">按钮</button>
<li v-for="stu in state.stus" :key="stu.id">{{ stu.name }}-{{ stu.id }}</li>
</div>
</template>
<script>
import { reactive, ref } from "vue";
export default {
name: "App",
//setup函数是组合API得入口函数
setup() {
//通过这个可知,组合API内部逻辑可以是其他函数,甚至其他js文件导出的逻辑
let { fn, count } = ctrlBtn();
//监听复杂对象,reactive的参数必须是对象或者数组
let state = reactive({
stus: [
{
id: 1,
name: "zq",
},
],
});
return {
state,
fn,
count,
};
},
};
function ctrlBtn() {
//定义名称叫做count得变量,这个变量得初始值是0
//ref函数只能监听简单类型的变化,不能监听复杂类型变化,例如数组,对象
let count = ref(0);
function fn() {
count.value += 1;
}
return { count, fn };
}
</script>
监听
- 默认情况下,无论是通过ref还是reactive都是递归监听
- 递归监听数据量比较大,消耗性能
- 非递归监听(shallowReactive,shallowRef),shallowReactive非递归监听是只监听第一层变化,只生成第一层的proxy对象(可以打印说明);但是如果修改第一层数据,同时内部的数据也被修改的话,可以发现UI界面都变化了,即使没被监听的也变化了,这是因为第一层修改了触发了render,如果不修改第一层数据,发现UI无变化。当然不论是递归还是不递归监听,数据都被变化,只是界面UI更新与否的差别
- 注意点:如果是通过shallowRef创建数据,那么vue监听的是.value的变化,并不是第一层数据的变化,即例如let state=shallowRef();其实是state被包装成了proxy。
- triggerRef:传入的参数会更新UI,一般用于,有的是非递归监听,但是我就想更新某个数据时候使用
- 本质:shallowRef(xx)=>shallowRef({value:xx}),监听的是.value的变化,因为value其实才是第一层
let state=shallowRef({
a:'a',
b:{
c:'c'
}
});
//a变化是不会更新界面的
//如下才行
state.value={....}
//triggerRef:主动更新,但是没有triggerReactive,如果是reactive类型数据是无法主动触发UI更新的
triggerRef(state);
toRaw
- 相当于reactive的逆向,获取的是传入的参数
- 应用场景是,不需要更新UI,但是想操作数据的时候
- 注意:如果是ref的数据,则需要.value形式才能拿到原始数据
let state = reactive({
name:'zq'
});
//state其实是把传入的对象包装成了一个proxy
//获取类型的原始数据
toRaw(state);//其实就是reactive传入的对象
//ref也是可以传递对象的
let obj={name:'zq'}
let ha=ref(obj);
toRaw(ha.value);
markRaw
- 作用是避免响应式,UI不会再更新了
let obj={name:'zq'}
obj=markRaw(obj);
let state=reactive(obj);
//后面再怎么更新,UI也不会有变化
toRef
把一个响应式对象转换成普通对象,该普通对象的每个property都是一个ref,和响应式对象property--对应
- 效果和ref类似,但是更多的应用场景是让局部数据变成响应式
- 不会触发UI界面的更新
- ref 复制,修改响应式数据不会影响以前的数据,数据变化UI更新
- toRef 引用,修改响应式数据会影响以前的数据,数据变化UI不更新
let obj={name:'zq',id:1}
let state=ref(obj.name);
function myFn(){
/*
如果利用ref将某一个对象中的属性变成响应式数据
如果修改响应式的数据是不会影响到原始数据的,即ref其实是复制的关系
*/
state.value='zs';
// obj 发现压根没变
//state 响应式对象是变化了
}
let obj={name:'zq',id:1}
let state=toRef(obj,'name');
function myFn(){
state.value='zs';
// obj/state 数据都变化了
/**
* 利用toRef将某一个对象中的属性变成响应式的数据,修改响应式的数据是会修改原始数据的
* 但是不会触发UI界面的更新
* /
}
toRefs
- 类似于toRef,但是是可以添加多个响应的
- toRef如果响应多个,是需要多次调用的
let obj={name:'zq',id:1}
let state=toRefs(obj);
function myFn(){
//注意此处value的位置
state.name.value='zs';
state.id.value=16;
}
customRef
- 自定义ref
- 应用场景:setup函数内部必须是同步操作,网络请求一般都是异步的,虽然回调可以使用,但是不优雅,这时候自定义ref的场景(最起码使用起来是同步的)就出现了
function myRef(value) {
return customRef((track,trigger)=>{
return {
get(){
track();//告诉vue,这个数据需要追踪变化
return value;
},
set(val){
value=val;
trigger();//告诉vue触发界面更新
}
}
})
}
setup() {
let age = myRef(18);
function myFn() {
age.value += 1;
}
return { age, myFn };
},
- 案例:网络请求
function myRef(value) {
return customRef((track, trigger) => {
fetch(value)
.then((res) => {
return res.json();
})
.then((data) => {
value = data;
trigger();
})
.catch((err) => {
console.log(err);
});
return {
get() {
track(); //告诉vue,这个数据需要追踪变化
//注意点:不能再get方法中发送网络请求,因为渲染界面->调用get->发送网络请求->保存数据->更新界面->调用get;形成死循环
return value;
},
set(val) {
value = val;
trigger(); //告诉vue触发界面更新
},
};
});
}
setup() {
//使用的时候,同步使用即可
let data = myRef("../public/data.json");
return { data };
},
组合API中监听生命周期
<template>
<div>
<button ref="btn">添加</button>
</div>
</template>
<script>
import { onMounted, ref } from "vue";
export default {
name: "HelloWorld",
setup() {
let btn=ref(null);
//组合API中监听生命周期
onMounted(()=>{
console.log('onMounted',btn.value); //<button>添加</button>
})
return { btn };
},
};
</script>
readonly
传入一个对象(响应式或普通)或ref,返回一个原始对象的只读代理,这个只读代理是深层的,对象内部任何嵌套的属性也都是只读的。
- 只读数据
- isReadonly:判断数据类型是不是readonly/shallowReadonly类型
- const :赋值保护,不能给变量重新赋值,但是对象内部修改可以修改
- readonly:属性保护,不能给属性重新赋值
- 注意: shallowReadonly :创建一个只读的数据,第一层只读
setup() {
//用于创建一个只读的数据,并且是递归只读(全部只读)
let state = readonly({ name: "zq" });
// state.name='asfsa' //警告:Set operation on key "name" failed: target is readonly
return { state };
},
手写Reactive/Ref等等
function shallowReactive(obj) {
return new Proxy(obj, {
get(ojb, key) {
return obj[key];
},
set(obj, key, val) {
obj[key] = val;
return true;
}
})
}
function shallowReadonly(obj) {
return new Proxy(obj, {
get(ojb, key) {
return obj[key];
},
set(obj, key, val) {
console.log(`${key}是只读的,不能赋值`);
}
})
}
//同理readonly其实就是把reactive的set改为输出即可
function shallowRef(val) {
return shallowReactive({ value: val });
}
function ref(val) {
return reactive({value:val})
}
function reactive(obj) {
if (typeof obj === 'object') {
if (obj instanceof Array) {
//如果是数组,取出数组中每一个元素,判断每个元素是否是对象
//如果又是对象也需要包装成proxy
obj.forEach(item => {
if (typeof item === 'object') {
obj[index] = reactive(item);
}
})
} else {
//如果是对象,取出对象属性的取值,判断对象属性的取值是否又是对象,如果还是对象则包装成proxy
for (const key in obj) {
let item = obj[key];
if (typeof item === 'object') {
obj[key] = reactive(item);
}
}
}
return new Proxy(obj, {
get(ojb, key) {
return obj[key];
},
set(obj, key, val) {
obj[key] = val;
return true;
}
})
} else {
console.log(`${obj} is not a object`);
}
}
TS搭建vue3.0开发环境
- 方案一:初始化项目时候选择自定义
- 方案二:进入已有项目,执行`vue add typescript
主要变化
- script标签添加ts
- export default后添加
defineComponent
<script lang="ts">
import { defineComponent } from 'vue';
import Home from "./components/Home.vue";
import News from "./components/News.vue";
export default defineComponent({
name: 'App',
components: {
Home,
News
},
data(){
return{
aaa:'ada'
}
}
});
</script>
基本使用
<template>
<p>{{title}}</p>
<button @click="setTitle">修改</button>
</template>
<script lang="ts">
import { defineComponent } from "vue";
let title:string="hahahh";
export default defineComponent({
data(){
return {
title
}
},
methods:{
setTitle(){
this.title="修改呢";
}
}
})
</script>
使用接口限制数据类型
<template>
<p>----{{username}}----</p>
<button @click="setUserName">改变</button>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from "vue";
interface User{
username:string,
age:number,
setUserName():void;
getUserName?():void;
}
export default defineComponent({
setup(){
//实现接口的第一种写法
let user:User=reactive({
age:20,
username:"张三",
setUserName(){
this.username="修改名字"
}
})
//实现接口的第二种写法
// let user=reactive<User>({
// age:20,
// username:"张三",
// setUserName(){
// this.username="修改名字"
// }
// })
//实现接口的第三种写法
// let user=reactive({
// age:20,
// username:"张三",
// setUserName(){
// this.username="修改名字"
// }
// }) as User
//普通写法
// let user=reactive({
// age:20,
// username:"张三",
// setUserName(){
// this.username="修改名字"
// }
// })
//泛型
// let count=ref<number|string>("20");
return{
...toRefs(user)
}
}
})
</script>
路由
npm install vue-router@next -S
- 新建src/routes.ts配置路由
import { createRouter,createWebHashHistory,createWebHistory } from "vue-router";
import Home from "./components/Home.vue";
import News from "./components/News.vue";
import NewsContent from "./components/NewsContent.vue";
const router=createRouter({
history:createWebHashHistory(),//hash模式
// history:createWebHistory(),//history模式
routes:[
{path:'/',component:Home},
{path:'/news',component:News},
{path:'/newscontent/:aid',component:NewsContent},//动态路由
]
})
export default router;
获取$router
import {getCurrentInstance} from 'vue'
setup(){
//const router=useRouter();//vue-router中的useRouter直接获取router对象
const {ctx}=getCurrentInstance();
ctx.$router.push('/about);
//或者直接:router.push('/about);
}
//获取动态路由参数
setup(){
const router=useRouter();
//或者ctx形式也行
console.log(router.currentRoute.value.params.id);
}
- main.ts
import { createApp } from 'vue'
import App from './App.vue'
import route from "./routes";
const app=createApp(App);
app.use(route);
app.mount('#app');
<template>
<router-link to="/">首页</router-link>
<router-link to="/news">新闻</router-link>
<router-view></router-view>
</template>
- News.vue
<template>
<ul>
<li v-for="(item,index) in list" :key="index">
<!--路由跳转-->
<router-link :to="`/newscontent/${index}`">{{item}}</router-link>
</li>
</ul>
</template>
<script lang="ts">
import { defineComponent} from "vue";
interface User{
list:string[]
}
export default defineComponent({
data(){
return {
list:[]
} as User;
},
mounted(){
for (let i = 0; i < 10; i++) {
this.list.push(`我是第${i}个新闻`)
}
}
})
</script>
- NewsContent.vue
<template>
<p>NewContents组件</p>
</template>
<script lang="ts">
import { defineComponent} from "vue";
export default defineComponent({
mounted(){
//获取动态路由传值
console.log(this.$route.params);
}
})
</script>
- Get获取参数
get参数传值:http://localhost:8080/#/newscontent?aid=1230
获取: this.$route.query
- 编程式路由
this.$router.push({path:'news'})
this.$router.push({path:'/newscontent/495'})
Vuex
setup(){
//const store =useStore();//vuex中的useStore直接获取store对象
const {ctx}=getCurrentInstance();
const storeCount=computed(()=>ctx.$store.state.count);
add(){
ctx.$store.commit('addMutation');
}
return{ storeCount}
}
//store/index.js
export default Vuex.createStore({
state:{count:1},
mutations:{
addMutation(state){
state.count++;
}
},
actions:{
},
modules:{
}
})
不能使用mapMutations,mapState....,因为依赖于this.$store;