vue3官网
vue3底层是用typescript写的能更好的支持ts。
Vue2和Vue3核心差异
Vue2:选项式API、Object.defineProperty响应式(仅监听对象属性/数组下标,需重写数组方法)、打包体积大;
Vue3:组合式API、Proxy响应式(监听整个对象/数组,无边界限制)、Tree-shaking按需打包体积-更小,新增Teleport/Suspense等特性。
响应式
Vue2:通过Object.defineProperty劫持对象属性的get/set,初始化递归遍历数据,缺陷:无法监听- 新增/删除属性、数组下标修改;
Vue3:通过Proxy代理整个目标对象,配合Reflect操作属性,天然支持新增/删除属性、数组变化,无-需递归初始化(懒代理)。
Vite
选择创建项目
- npm create vite@latest
- npm init vite@latest
- npm init vue@latest (官网推荐使用)
vite 创建的项目常用配置的地方
Network: use --host to expose
可以通过下面两种方法的任意一种:
1、修改vite.config.js文件
server: {
host: '0.0.0.0',
host: true, // 这是上面的缩写形式
}
2、修改package.json
"dev": "vite --host 0.0.0.0",
"dev": "vite --host", // 这是上面的缩写形式
Vue CLI
- 卸载旧版本:
- npm uninstall vue-cli -g
- yarn global remove vue-cli
- 安装脚手架:
- npm install -g @vue/cli
- yarn global add @vue/cli
ts、tsx
<script lang="ts"></script>
TypeScript 与 JSX `render` 函数]结合起来:
<script lang="tsx"></script>
全局
import { createApp } from 'vue'
const app = createApp({})
globalProperties对象
app.config.globalProperties.$http = () => {}
app.config.globalProperties.$name = 'xxx'
在template模板中使用:{{$name}}
js中使用:
import { getCurrentInstance } from "vue";
const GCI = getCurrentInstance();
console.log(GCI.appContext.config.globalProperties.$name);
console.log(GCI.proxy.$name);
注册组件:app.component()
指令:app.direction('', {
created() {},
// 在绑定元素的父组件挂载之前调用
beforeMount() {},
// 在绑定元素的父组件挂载之后调用
mounted() {},
// 在包含组件的 VNode 更新之前调用
beforeUpdate() {},
// 在包含组件的 VNode 及其子组件的 VNode 更新之后调用
updated() {},
// 在绑定元素的父组件卸载之前调用
beforeUnmount() {},
// 在绑定元素的父组件卸载之后调用
unmounted() {}
})
defineComponent 定义组件
import { defineComponent } from 'vue'
// 只有一个setup函数,可以这样简写
export default defineComponent(() => {
return { }
})
defineAsyncComponent 定义异步组件、Suspense
// 引入组件
import { defineAsyncComponent } from 'vue';
const Child = defineAsyncComponent(() => import('@/components/Child.vue'))
// 使用需要Suspense内置组件包裹
<Suspense>
<template #default>
<Child />
</template>
<template #fallback>
<h3>加载中...</h3>
</template>
</Suspense>
teleport
<teleport to="#app"></teleport>
<teleport to=".lxh"></teleport>
<teleport to="body"></teleport>
useRouter、useRoute
import { useRouter, useRoute } from "vue-router";
const router = useRouter()
router.push({path: '',query: {}})
const route = useRoute()
onMount(() => {
console.log(route)
})
ref、reactive
import { ref, reactive } from 'vue';
// 定义数据
let str1 = ref('lxh');
let str2 = ref({name: 'lxh'});
// 修改
str1.value = 'LXH';
str2.value.name = 'LXH';
// 定义复杂数据类型,不能定义基本数据类型
let obj = reactive({name: 'lxh'});
// 修改
obj.name = 'LXH';
setup
// setup在beforeCreate之前执行,所有setup里没有this
/**
* 接收两个参数
* props:在props里接收了数据,才能在setup里获取到数据,props是只读属性
* context:emit、attrs、slots
*/
setup(props, context) {
return {}
}
computed、watch、watchEffect
import { computed, watch, watchEffect } from 'vue';
const com = computed(() => {})
// 监听ref定义的数据
watch(xxx, (newValue, oldValue) => {}, {immediate: true, deep: true})
watch([xxx, yyy], (newValue, oldValue) => {})
// 监听reactive定义的数据,强制开启了深度监听,所以设置deep无用
watch(xxx, (newValue, oldValue) => {}, {immediate: true})
watch(() => xxx.name, (newValue, oldValue) => {})
watch([() => xxx.name, () => xxx.age], () => {})
// 停止监听的用法
const stopWatch = watch(xxx, (newValue, oldValue) => {
newValue > 25 ? stopWatch : console.log(newValue)
})
// watch开启了immediate和watchEffect的顺序,谁写在前面就先执行谁
// watchEffect里用到哪个属性,就监听哪个属性,在onBeforeMount之前执行
watchEffect(() => {})
获取组件实例
// 获取当前组件的实例
import { getCurrentInstance } from 'vue'
// 获取子组件的实例3.2版本
<child ref='lxhRef' />
import { ref } from 'vue';
const lxhRef = ref();
console.log(lxhRef.value); // 获取到实例
vue3.2
defineProps、defineEmits
<script setup>
const props = defineProps({
xxx: String
})
在template模板中使用:{{xxx}}
在js中使用:props.xxx
const emit = defineEmits(['yyy'])
使用:emit('yyy', '传给父组件的数据')
</script>
<script setup lang='ts'>
const props = defineProps<{
foo: string
bar?: number
}>()
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
// 使用类型声明时的默认 props 值
interface Props {
msg?: string
labels?: string[]
}
// withDefaults
const props = withDefaults(defineProps<Props>(), {
msg: 'hello',
labels: () => ['one', 'two']
})
</script>
defineExpose
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
defineExpose({
a,
b
})
</script>
useSlots 和 useAttrs
<script setup>
import { useSlots, useAttrs } from 'vue'
const slots = useSlots()
const attrs = useAttrs() // 相当于vue2中的this.$attrs
</script>
组件通信
父子组件
vue3移除了.sync,用v-model:替代
// 父组件
<Child v-model='msg' v-model:xxx='xxx'></Child>
const msg = ref('parent data1');
const xxx = ref('parent data2');
// 子组件
<div>{{ modelValue }}</div>
<button @click="emit('update:modelValue', 'child data1')">change</button>
<button @click="emit('update:xxx', 'child data2')">change</button>
const props = defineProps({ modelValue: String, xxx: String });
const emit = defineEmits(['update:modelValue', 'update:xxx']);
v-if优先级高于v-for
pinia
vuex的替代者,拥有更简洁的语法,扁平化的代码编排,更符合vue3组合式api
基本使用
安装:npm i pinia
在main.js文件里注册使用:
import { createPinia } from "pinia"
const APP = createApp(App)
APP.use(createPinia())
在store/index.js文件,多个可以互相调用
import { defineStore } from 'pinia'
export const useStore = defineStore('store', {
state: () => ({
count: 0,
}),
getters: {
formatterCount(state) {
// return `数字是${state.count}`
return `数字是${this.count}`
}
},
actions: {
change() {
this.count++
// this.$patch({
// count: this.count + 1
// })
// this.$patch((state) => {
// state.count = state.count + 1
// state.count = this.count + 1
// })
}
},
})
在组件中使用
<script setup>
// storeToRefs解构让值具有响应式
import { storeToRefs } from "pinia";
import { useStore } from "../store";
const store = useStore();
const { count, formatterCount } = storeToRefs(store);
// 修改单个
const btn = () => {
store.count++;
};
// 修改多个
const batchBtn = () => {
// 对象形式
// store.$patch({
// count: store.count + 1,
// });
// 函数形式
store.$patch((state) => {
state.count = state.count + 1;
});
};
</script>
<template>
<div>{{count}} -- {{ formatterCount }}</div> <!-- 显示 -->
<button @click='count++'>修改</botton> <!-- 修改 -->
<button @click='btn'>修改</botton>
<button @click="batchBtn">批量修改</button>
<button @click="store.change()">通过actions修改</button>
</template>
模块化
user.js文件
import { defineStore } from 'pinia'
export default defineStore('user', {
state: () => ({
obj: {
name: 'xxx',
age: 18
},
})
})
table.js文件
import { defineStore } from 'pinia'
export default defineStore('table', {
state: () => ({
arr: [1, 2, 3]
})
})
store/index.js文件
import table from "./modules/table";
import user from "./modules/user";
export default () => ({
user: user(),
table: table(),
})
在组件中使用
<script setup>
import { storeToRefs } from "pinia";
import store from "../store";
const { table, user } = store();
const { obj } = storeToRefs(user);
const { arr } = storeToRefs(table);
const btn = () => {
user.obj.age++;
};
const batchBtn = () => {
table.$patch((state) => {
state.arr = [...state.arr, 4];
});
};
</script>
<template>
<div>hello world -- {{ obj.name }} -- {{ obj.age }} -- {{ arr }}</div>
<button @click="obj.name = 'yyy'">修改</button>
<button @click="obj.age++">修改</button>
<button @click="btn">修改</button>
<button @click="batchBtn">批量修改</button>
</template>
<style scoped></style>
持久化
插件pinia-plugin-persistedstate
安装:npm i pinia-plugin-persistedstate
在main.js文件注册使用
import { createPinia } from "pinia"
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const APP = createApp(App)
APP.use(createPinia().use(piniaPluginPersistedstate))
APP.mount('#app')
user.js文件
import { defineStore } from 'pinia'
export default defineStore('user', {
persist: true, // 开启持久化
state: () => ({obj: {}}),
})
持久化存储插件其他配置
persist: {
// 修改存储中使用的键名称,默认为当前 Store的 id 如:user
key: 'pinia',
// 修改为 sessionStorage,默认为 localStorage
storage: window.sessionStorage,
// 部分持久化状态的点符号路径数组,[]意味着没有状态被持久化(默认为undefined,持久化整个状态),一般不用
paths: ['obj'],
},
vite使用scss
默认没有安装
安装
npm i sass sass-loader -D
配置
在vite.config.js文件里配置
css: {
preprocessorOptions: {
scss: {
additionalData: "@import '@/assets/reset.scss';"
}
}
}