npm init @vitejs/app vue3-vite-demo
打开文件,建立router路由文件,建立index.js文件
import { createRouter, createWebHashHistory } from "vue-router";
import globalRoutes from "./globalRoutes";
import mainRoutes from "./mainRoutes";
const router = createRouter({
history: createWebHashHistory(),
scrollBehavior: () => ({ y: 0 }),
isAddDynamicMenuRoutes: false, // 是否已经添加动态(菜单)路由
routes: globalRoutes.concat(mainRoutes),
});
export default router;
安装路由命令
npm install vue-router@4
/或
npm install vuex@next --save
运行项目可能出现报错,是因为安装的vue-router版本是3及以下。要安装v4版本还有createRouter等Api。如果安装了还是有报错,关闭项目,重启服务器即可。
路由建立好了之后在main.js里引用
这时候路由引用完毕。
路由跳转
import { useRouter } from "vue-router";
const router = useRouter();
router.push({ name: "" });
另开一个页面的路由跳转方式
const { href } = router.resolve({
name: "softwares",
query: { basicInfoId: row.basicInfoId },
});
window.open(href, "_blank");
修改Vite的配置文件,支持alias别名@(路径名指定@为src)
import { defineConfig } from 'vite'
import path from 'path'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
}
},
plugins: [vue()]
})
安装vuex
npm install vuex@next --save
import { createStore } from 'vuex'
export default createStore({
state: {
name: ''
},
mutations: {
name: (state, newValue) => {
state.name = newValue
}
},
actions: {
setName: (ctx, value) => {
console.log(ctx);
console.log(value);
ctx.commit('name', value)
}
}
})
页面中使用
使用模块化vuex
src\store\index.js建立模块化
建立common模块
全局通用模块,可要可不要
页面中使用
npm install element-plus --save
完整引入和按需引入参照官网https://element-plus.gitee.io/zh-CN/guide/quickstart.html
reactive 和 ref 都是用来定义响应式数据的, reactive更推荐去定义复杂的数据类型, ref 更推荐定义基本类型
ref 和 reactive 本质我们可以简单的理解为ref是对reactive的二次包装, ref定义的数据访问的时候要多一个.value
const data=reactive({
name:"zhangyi",
username:computed(()=>{
return data.name.slice(1,2)
})
})
监听reactive响应式数据中的某个属性,要将箭头函数写成函数return的方式才有效。
如果监视的是reactive定义的响应式数据,则无法正确获得oldValue!!!
watch 和 watchEffect 都是侦听器,但是写法和使用上有一些区别。
watch:
1.具有一定的惰性lazy 第一次页面展示的时候不会执行,只有数据变化的时候才会执行
2.参数可以拿到当前值和原始值
3.可以侦听多个数据的变化,用一个侦听起承载
watchEffect :
没有过多的参数 只有一个回调函数
1.立即执行,没有惰性,页面的首次加载就会执行。
2.自动检测内部代码,代码中有依赖 便会执行
3.不需要传递要侦听的内容 会自动感知代码依赖,不需要传递很多参数,只要传递一个回调函数
4.不能获取之前数据的值 只能获取当前值
5.一些=异步的操作放在这里会更加合适
watch:{
obj:{
handle(newVal,oldVal){
console.log(newVal,oldVal)
},
immediate:true,
deep:true
}
}
子组件
父组件
vue2中sync写法在vue3中的使用。(父子组件传值响应式,即子组件发生改变,父组件同步改变)
父组件
必须使用v-model形式传值给子组件
子组件
先通过defineProps接受传递过来的name,然后通过defineEmits和update:name使用父子数据双向绑定
注:上方写错了。const emit = defineEmits([“name”])修改为上方const emit = defineEmits([“update:name”])
Provide/inject用于不限层级的传值,父组件中Provide参数,子或孙组件都可通过inject拿到参数值。
父组件
子组件
可以通过useContext从上下文中获取 slots 和 attrs。不过提案在正式通过后,废除了这个语法,被拆分成了useAttrs和useSlots。示例:
// 旧
<script setup>
import { useContext } from 'vue'
const { slots, attrs } = useContext()
</script>
// 新
<script setup>
import { useAttrs, useSlots } from 'vue'
const slots = useSlots();
const attrs = useAttrs();
console.log(attrs); // 查看父组件传来的自定义属性
</script>
attrs
父组件的标签可以加各种"属性",能和props对应上的就变成了子组件的props,对应不上的就都是attrs,不限于函数,各种类型都行。
如果没有声明defineProps接收父传递过来的属性,则由useAttrs接收。如果声明了defineProps接收,则useAttrs为空。
console.log(attrs)打印如下
子组件使用defineExpose对外暴露属性
父组件可接受到子组件暴露出来的属性
toRef 是将某个对象中的某个值转化为响应式数据,其接收两个参数,第一个参数为 obj 对象;第二个参数为对象中的属性名。
区别:
ref 是对传入数据的拷贝;toRef 是对传入数据的引用
ref 的值改变会更新视图;toRef 的值改变不会更新视图
toRefs作用就是将传入的对象里所有的属性的值都转化为响应式数据对象,该函数支持一个参数,即 obj 对象。
shallowReactive这是一个浅层的 reactive,原本的 reactive 是深层的,shallowReactive这是一个用于性能优化的API。优化reactive
const obj = {
a: 1,
first: {
b: 2,
second: {
c: 3
}
}
}
//如果使用reactive包裹,则obj的每一层都是proxy响应式数据对象,
//某些情况下对性能有影响
const state = reactive(obj)
//如果使用shallowReactive包裹,则只有第一层a:1是proxy响应式数据对象,
//只有第一层a发生改变了才会触发页面改变,b、c改变只会改变值,
//不会触发视图更新。
const state = shallowReactive(obj)
shallowRef,这是一个浅层的 ref,与 shallowReactive 一样是拿来做性能优化的,优化ref
shallowReactive 是监听对象第一层的数据变化用于驱动视图更新,那么 shallowRef 则是监听 .value 的值的变化来更新视图的
可以看出在change2方法里通过.value改变属性的方式不会触发视图更新。但是可以引入triggerRef方法触发视图更新
toRaw 方法是用于获取 ref 或 reactive 对象的原始数据的
可以看出reactive数据会使原始数据也发生改变。如果要获取原始数据,则通过toRaw方法获取:
注意: 补充一句,当 toRaw 方法接收的参数是 ref 对象时,需要加上 .value 才能获取到原始数据对象
markRaw 方法可以将原始数据标记为非响应式的,即使用 ref 或 reactive 将其包装,仍无法实现数据响应式,其接收一个参数,即原始数据,并返回被标记后的数据。
代码中可以看到,被markRaw包裹的obj再进行reactive包裹,也不会成为响应式数据。
Vue2的任何一个组件中想要获取当前组件的实例可以通过 this 来得到,而在Vue3中我们大量的代码都在 setup 函数中运行,并且在该函数中 this 指向的是 undefined,所以这里要使用getCurrentInstance方法获取实例
ctx 和 proxy 的内容十分类似,只是后者相对于前者外部包装了一层 proxy,由此可说明 proxy 是响应式的。
**注意:**开发环境下才能获取到当前组件的实例,换句话说就是这个方法只是在开发环境下用于调试使用的;在生产环境下拿不到实例。 大家不要依赖 getCurrentInstance 方法去获取组件实例来完成一些主要功能,否则在项目打包后,一定会报错的。
在Vue2中,我们获取元素都是通过给元素一个 ref 属性,然后通过 this.$refs.xx 来访问的,但这在Vue3中已经不再适用了
<template>
<span>zhangyi</span>
</template>
<script setup>
import { reactive } from 'vue'
const state = reactive({
color: 'red'
})
</script>
<style scoped>
span {
// 使用v-bind绑定state中的变量
color: v-bind('state.color');
}
</style>
描述:作用域插槽其实就是带数据的插槽,即带参数的插槽,简单的来说就是子组件提供给父组件的参数,该参数仅限于插槽中使用,父组件可根据子组件传过来的插槽数据来进行不同的方式展现和填充插槽内容。
插槽名可以是动态变化的v-slot:[slotName]
以下是vue2的插槽,在vue2.6中。下述的API被软废弃(3.0正式废弃),取而代之的是内置指令v-slot,可以缩写为【#】
// Parent.vue
<child>
<!-- 默认插槽 -->
<div>默认插槽</div>
<!-- 具名插槽 -->
<div slot="header">具名插槽header</div>
<!-- 作用域插槽 -->
//data同上,是子组件传递过来的值
<div slot="footer" slot-scope="data">
{{data.name}}
</div>
</child>
官网地址:https://pinia.vuejs.org/
Pinia.js是由Vue.js团队核心成员开发的新一代状态管理器,使用Composition Api进行重新设计的,也被视为下一代Vuex。Pinia是一个Vue的状态管理库,允许跨组件、跨页面进行全局共享状态,也由于其设计的简洁性、和对typescript的良好支持,取代Vuex指日可待。
Pinia.js 有如下特点:
1、完整的 typescript 的支持;
2、足够轻量,压缩后的体积只有1.6kb;
3、去除 mutations,只有 state,getters,actions(这是我最喜欢的一个特点);
4、actions 支持同步和异步;
5、没有模块嵌套,只有 store 的概念,store 之间可以自由使用,更好的代码分割;
6、无需手动添加 store,store 一旦创建便会自动添加;
$ yarn add pinia
$ npm i pinia
$ pnpm i pinia
//main.js
import { createPinia } from 'pinia'
const pinia = createPinia()
app.use(pinia)
//src/store/index.js
import { defineStore } from 'pinia';
export const mainStore = defineStore("main", {
state() {
return {
name: 'hello pinia',
age: 20
}
},
getters: {
get() {
return this.name + "," + 'zhangyi';
}
},
actions: {
increment() {
this.age++;
}
}
})
但一般不建议store.age++直接修改,建议通过 actions 去修改 state,action 里可以直接通过 this 访问。
<template>
<div>
<div> {{store.age}}</div>
<div> {{store.name}}</div>
<!-- 使用store里的getter -->
<div>{{store.get}}</div>
<div><button @click="add"> add</button></div>
<div><button @click="addObj"> addObj</button></div>
<div><button @click="addFun"> addFun</button></div>
<div><button @click="addAction"> addAction</button></div>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from "vue";
import { mainStore } from "@/store/index";
const store = mainStore();
//单条数据修改
const add = () => {
store.age++;
console.log(store.age);
};
//多条数据修改--对象形式
const addObj = () => {
store.$patch({
age: store.age + 2,
name: "zhangyi",
});
};
//多条数据修改--函数形式
const addFun = () => {
store.$patch((state: any) => {
state.age = store.age + 2;
state.name = "zhangyi";
});
};
//通过action修改
const addAction = () => {
store.increment();
};
</script>
<style lang='less' scoped>
</style>
解构store
当store中的多个参数需要被使用到的时候,为了更简洁的使用这些变量,我们通常采用结构的方式一次性获取所有的变量名
<template>
<div>
<div>ES传统方式解构(能获取到值,但是不具有响应性)</div>
<div> {{age}}</div>
<div> {{name}}</div>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from "vue";
import { mainStore } from "@/store/index";
const store = mainStore();
const { name, age } = store;
</script>
<style lang='less' scoped>
</style>
Pinia解构方法:storeToRefs
应该像composition API使用props一样,从pinia中导出storeToRefs进行获取响应式数据。
<template>
<div>
<div>Pinia解构方法:storeToRefs</div>
<div> {{age}}</div>
<div> {{name}}</div>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from "vue";
import { mainStore } from "@/store/index";
import { storeToRefs } from "pinia";
const store = mainStore();
const { name, age } = storeToRefs(store);
</script>
<style lang='less' scoped>
</style>
import { defineStore } from 'pinia';
export const allanStore = defineStore("allan", {
state() {
return {
moveList: ["蜘蛛侠"]
}
},
getters: {},
actions: {}
})
在原 store 中引入 allanStore,并获取 moveList
import { defineStore } from 'pinia';
import { allanStore } from './allanStore'
export const mainStore = defineStore("main", {
state() {
return {
name: 'hello pinia',
age: 20
}
},
getters: {
get() {
return allanStore().moveList;
}
},
actions: {
increment() {
this.age++;
}
}
})
插件 pinia-plugin-persist 可以辅助实现数据持久化功能。
npm i pinia-plugin-persist --save
使用
// main.js
import { createPinia } from 'pinia'
import piniaPluginPersist from 'pinia-plugin-persist'
const pinia = createPinia()
pinia.use(piniaPluginPersist)
接着在对应的 store 里开启 persist 即可。
import { defineStore } from 'pinia';
export const mainStore = defineStore("main", {
state() {
return {
name: 'hello pinia',
age: 20
}
},
getters: {
get() {
return this.name + "," + 'zhangyi';
}
},
actions: {
increment() {
this.age++;
}
},
// 开启数据缓存
persist: {
enabled: true
}
})
自定义 key
可以在 strategies 里自定义 key 值,并将存放位置由 sessionStorage 改为 localStorage。
persist: {
enabled: true,
strategies: [
{
key: 'main',
storage: localStorage,
}
]
}
持久化 state中某个变量
state: () => {
return {
name: '张三',
age: 18,
gender: '男'
}
},
persist: {
enabled: true,
strategies: [
{
storage: localStorage,
paths: ['name', 'age']
}
]
}
上面我们只持久化 name 和 age,并将其改为localStorage, 而 gender 不会被持久化,如果其状态发送更改,页面刷新时将会丢失,重新回到初始状态,而 name 和 age 则不会。
命令
npm init @vitejs/app 项目名称
然后把vue3的src删掉,把vue-cli创建的vue3里面的src文件复制粘贴过来。再把package.json复制粘贴过来即可。
unplugin-auto-import
解决场景:在组件开发中无需每次都引入import { ref,onmounted…}等等
1、下载安装
npm i unplugin-auto-import -D
/*
* @Descripttion:
* @Date: 2022-09-20 16:30:03
*/
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from "unplugin-auto-import/vite"
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
imports: ["vue", "vue-router"]//自动导入相关函数
})
]
})
还没写完。补充ing