初识 Vue3
cli 创建项目时可直接选择安装 sass loader 、 vue router 、 Vuex , vite 则需项目创建完成后手动配置 此外 vite 提供了对 .scss , ....
项目创建
Vue2
-
$ vue create hello-world
Vue3
-
$ npm init vite@latest
cli创建项目时可直接选择安装sass-loader、vue-router、Vuex,vite则需项目创建完成后手动配置此外
vite提供了对.scss,.sass,.less,.styl和.stylus文件的内置支持,无需添加响应loader只需添加响应预处理器依赖即可
# .scss and .sass
$ npm install -D sass
# .less
$ npm install -D less
项目基础配置
打包配置
Vue2
Vue3
环境变量配置
全局环境变量均配置于项目.env文件中,此处区别于cli中自定义变量需以VUE_APP_为前缀,参考 cli 环境变量;vite中以VITE_为前缀定义,参考 vite 环境变量。
使用方式也存在区别,cli 中使用process.env.VUE_APP_***方式获取变量值;vite中使用import.meta.env.VITE_***。
生命周期
Vue2
Vue3
选项式/组合式
Vue2中的写法即选项式Api方式
<script>
export default {
name: 'Test',
data () {
return {
message: 'TestMessage',
}
},
methods: {
handleClick () {
console.log(this.message);
},
}
}
</script>
选项式Api即导出为一个对象,内部通过配置各选项实现功能,例
name、data、methods等选项。
Vue3中推荐使用的写法为组合式写法
<script setup>
const message = ref('TestMessage');
const handleClick = () => {
console.log(message.value);
}
</script>
总结
选项式Api:
优点:由于各选项职责明确所以更易于学习和使用
缺点:代码组织性相对较差,相似的逻辑代码不便于复用且逻辑复杂度较高时代码可读性会降低
组合式Api:
优点:功能逻辑复杂时,各个功能逻辑代码组织在一起,便于阅读和维护
缺点:对于编写代码者本身有一定的代码组织能力的要求,需要有良好的代码组织能力和拆分逻辑能力
组合式 API 的出现是为Vue新增赋能,并不是为了取代选项式 API,故而
Vue3中仍可使用选项式代码风格进行编写
响应式数据
<template>
<span>{{obj.a}}</span>
</template>
<script>
export default {
data () {
return {
obj: {
a: ''
},
};
},
methods: {
appendParam () {
this.$set(this.obj, 'b', 2);
},
}
}
</script>
<template>
<span @click="objRef.b = 1">{{objRef.b}}</span>
</template>
<script setup>
const obj = reactive({a: 1});
const appendParam = () => {
obj.b = 2;
console.log(obj.b)
};
const setObj = () => {
Object.assign(obj, {c: 3});
};
const cleanObj = () => {
Object.keys(obj).forEach(key => delete obj[key]);
};
const objRef = ref({});
const setObjRef = () => {
objRef.value = {a: 1, b: 2};
};
const cleanObjRef = () => {
objRef.value = {};
console.log(obj.value.b)
};
class ObjModel = {
a = 'a'
b = 'b'
}
const objModel = reactive(new ObjModel())
const setObjModel = () => {
objModel.c = 'c'
}
const resetObjModel = () => {
Object.assign(objModel, new ObjModel());
}
const arr = reactive([]);
const changeArr = () => {
arr.length = 0;
arr.push(...[1, 2, 3]);
};
</script>
组件
Vue 2
<script>
export default {
name: 'TestComponent',
model: {
prop: 'value',
event: 'change'
},
props: {
value: {
type: Boolean,
default: false
},
message: {
type: String,
default: 'default-message'
}
},
methods: {
handleClick () {
this.$emit('customClick', 'text click')
this.$emit('change', !this.value);
}
}
}
</script>
Vue 3
<script setup>
const props = defineProps({
modelValue: {
type: Boolean,
default: false,
},
message: {
type: String,
default: 'default-message'
}
});
const emits = defineEmits(['customClick', 'update:modelValue'])
const handleClick = () => {
emits('customClick', 'text click')
emits('update:modelValue', !props.modelValue)
}
const exposeFun = () => {
console.log("I'm public function")
}
defineExpose({
exposeFun,
});
</script>
组件 Ref 区别
<template>
<input ref='input'/>
</template>
<script>
export default {
mounted() {
this.$refs.input.focus();
}
}
</script>
<template>
<input ref='input'/>
</template>
<script setup>
const input = ref(null)
const fun = () => {
input.value?.focus()
}
onMounted(() => {
input.value.focus()
})
</script>
路由
基础用法
<script>
export default {
created () {
console.log(this.$route, this.$router)
},
}
</script>
<script setup>
const route = useRoute();
const router = useRouter();
</script>
KeepAlive区别
<template>
<transition>
<keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</transition>
</template>
<template>
<router-view v-slot="{ Component, route }">
<keep-alive :include="cachedViews">
<component :is="Component" :key="route.path" />
</keep-alive>
</router-view>
</template>
全局状态管理
Vuex
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
todos: [
{ id: 1, text: 'todo item done', done: true },
{ id: 2, text: 'todo item not done', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
},
mutations: {
addTodo (state, todo) {
state.todos.push(todo);
}
},
actions: {
addTodo ({state, commit}, todo) {
commit('addTodo', todo)
}
},
modules: {}
})
Pinia
import { createPinia } from 'pinia'
app.use(createPinia())
import { defineStore } from 'pinia'
export const useTodosStore = defineStore('todos', {
state: () => ({
todos: [
{ id: 1, text: 'todo item done', done: true },
{ id: 2, text: 'todo item not done', done: false }
]
}),
getters: {
doneTodos (state) {
return state.todos.filter(todo => todo.done)
},
},
actions: {
addTodo(text) {
this.todos.push({ text, id: 3, done: false })
},
}
})
<script setup>
import { useTodosStore } from '@/stores/todos'
const todoStore = useTodosStore();
console.log(todoStore.doneTodos);
// 更新数据
todoStore.todos.push({ text: '外部添加', id: 4, done: false });
todoStore.todos = [{ text: '外部添加', id: 4, done: false }];
// 或
todoStore.$patch({ todos: [] });
// 或
todoStore.addTodo({ text: '外部添加', id: 4, done: false });
</script>
计算属性
<script>
export default {
data () {
return {
count: 1,
};
},
computed: {
doubleCount () {
return this.count * 2;
}
}
}
</script>
<script setup>
const count = ref(1);
const doubleCount = computed(() => count.value * 2);
</script>
侦听器
<script>
export default {
data () {
return {
count: 1,
countObj: {
count: 1,
}
};
},
watch: {
count (val) {
console.log('watch - count', val);
},
'countObj.count' (val) {
console.log('watch - countObj.count', val);
},
}
}
</script>
<script setup>
const count = ref(1);
const countObj = reactive({count: 1});
watch(count, (val) => {
console.log('watch - count', val);
});
watch(() => countObj.count, (val) => {
console.log('watch - countObj.count', val);
});
</script>
可复用性
混入
// src/mixins/logMessage.js
export default {
methods: {
logMessage (message) {
console.log(message)
},
}
};
<script>
import LogMessage from '@/mixins/logMessage';
export default {
mixins: [LogMessage],
mounted () {
this.logMessage('mounted')
}
}
</script>
Vue3中虽保留了对于mixin的支持但也仅仅是为了兼容老版本及各依赖库,对于 Vue3 来说还是建议用 组合式函数 来实现复用部分代码。
// logMessage.js
export function useMessage() {
const logMessage = (message) => {
console.log(message)
};
return { logMessage }
}
<script setup>
import { useMessage } from './logMessage.js'
const { logMessage } = useMessage();
onMounted(() => {
logMessage('mounted')
});
</script>
自定义指令
export default {
bind (el, binding, vnode) {},
inserted () {},
update () {},
componentUpdated () {},
unbind () {},
}
export default {
created(el, binding, vnode, prevVnode) {},
beforeMount() {},
mounted() {},
beforeUpdate() {},
updated() {},
beforeUnmount() {},
unmounted() {}
}