vue3
1.使用vue-cli创建vue3工程
- 查看vue-cli 版本, vue -V,vue-cli 4.5.0 以上
- 安装最新的vue-cli, npm i -g @vue/cli --force (强制覆盖之前版本)
- vue create 项目名
- 关闭eslint , 根目录添加vue.config.js 【与package.json文件同级】
module.exports = {
lintOnSave: false
}
- 安装vue开发工具,谷歌应用商店或在线下载, vue(beta), 目前不支持vuex
2.vue3 实现响应式 VS vue2 实现响应式
2.1 vue2
const data = { name: "bwf", age: 18, }
const _data = {}
function init(data) {
for (const key of Object.keys(data)) {
Object.defineProperty(_data, key, {
configurable: true,
get() {
console.log('get');
return data[key]
},
set(newValue) {
console.log('set', newValue);
data[key] = newValue
}
})
}
}
init(data)
2.2 vue3
const p = new Proxy(data, {
get(target, properName) {
console.log('get', target, properName);
return target[properName]
},
set(target, properName, value) {
console.log('set', target, properName, value);
target[properName] = value
},
deleteProperty(target, properName) {
console.log('deleteProperty', target, properName);
return delete target[properName]
}
})
2.3 vue3 vs vue2
3. vue3中常见api
3.1 template
template: v-指令 / 插槽 / 模板语法 / 事件绑定 / 父子属性传值 和 vue2一样
3.2 setup
[1]本质是一个函数 返回出去的数据以及方法才能被页面获取到
[2]执行时机: 在beforeCreate之前执行1次(此时this是undefined); 注意props改变也不再执行
[3]接收到的参数:
props 父组件传过来的属性数据,值得注意的是子组件最好也要有props配置项(写法同vue2)
context {emit: 触发父事件, slots: 父组件传过来的插槽}
3.3 响应式函数
ref(): 一般用于处理基本类型数据,除模板外,任何读写的地方都要 .value
reactive():处理引用类型数据,删除添加对象的属性 以及 直接改变数组某一项都是响应式的,不用.value
3.4 常见的生命周期函数 【兼容vue2写法】
onMounted: dom渲染完成 onBeforeUnmount:页面卸载之前
3.5 computed 计算属性
setup里面可以注册多个computed 【兼容vue2写法,写在配置项里面】
执行时机:
前提是计算属性有放到 template;
第一次执行 在created钩子之后,mounted钩子之前触发 ;
后面每次执行是只要是 computed里面的响应式的数据发生改变就会执行;
3.6 watch 侦听属性
watch([sum, msg], (newValue, oldValue) => {
console.log('watch---sum', newValue, oldValue)
}, { immediate: true })
watch(person, (newValue, oldValue) => {
}, { deep: false })
watch(() => person.name, (newValue, oldValue) => {
})
3.7 watchEffect
类似computed, 但不接收返回值
执行时机:
前提是计算属性有放到 template
第一次执行 在created钩子之后,mounted钩子之前触发 ;
后面每次执行是只要是 watchEffect 里面的响应式的数据发生改变就会执行;
watchEffect(() => {
console.log('watchEffect', person.name, person.age);
})
3.8 provide / inject 父代向后代传递数据 / 接收数据
let job = reactive(
{ salary: 9, address: "上海" }
)
provide('appData', job)
const appData = inject('appData');
4.演示demo
- App是祖先组件,主要演示setup、ref()、reactive()、生命周期函数、provide、向ComputedDemo子组件传递数据和事件
- ComputedDemo组件演示computed、接收App父组件传递的数据和调用父组件身上注册的事件
- WatchDemo组件演示watch、watchEffect
- ChildChild组件演示inject
4.1 App组件
<template>
<div class="app-page">
<div>
<h3>App page</h3>
<p>年龄{{age}}</p>
<button @click="addAge">点我年龄 + 1</button>
<p>工作信息:地点:{{job.address}}, <span style="color:red;font-weight:700">薪资:{{job.salary}}k</span> </p>
<button @click="addSalary">点我薪资+1</button>
<p>属性sex:{{job.sex}}</p>
<button @click="addSexPropery">添加/切换属性sex</button>
<ul>
<li v-for="(item,index) in list" :key="index">
{{item.name}}
</li>
</ul>
<button @click="editList">点我修改数组第1项</button>
<p>ComputedDemo page子组件给父组件传过来的 <span style="color:red;font-weight:700">name:{{name}}</span></p>
</div>
<computed-demo :job="job" @handleName="handleName">
<template v-slot:slot1>
<span>父组件传过来的----node1</span>
</template>
<template v-slot:slot2>
<span>父组件传过来的----node2</span>
</template>
</computed-demo>
<watch-demo></watch-demo>
</div>
</template>
<script>
import ComputedDemo from './components/ComputedDemo'
import WatchDemo from './components/WatchDemo'
import { ref, reactive, onMounted, onBeforeUnmount, computed, provide } from 'vue'
export default {
name: 'App',
components: {
ComputedDemo,
WatchDemo
},
beforeCreate () {
console.log('beforeCreate')
},
setup (props, context) {
console.log("App---setup", props)
let age = ref(90);
let name = ref("");
let job = reactive(
{ salary: 9, address: "上海" }
)
const list = reactive([{ name: "bwf" }, { name: "wmy" }])
provide('appData', job)
onMounted(() => {
console.log("app onMounted")
})
const addAge = () => {
age.value++
}
const addSalary = () => {
job.salary++
}
const handleName = (payloads) => {
console.log("父组件定义的方法被子组件emit方式触发了,子组件传过来的数据是", payloads)
name.value = payloads.value
}
const editList = () => {
list[0] = { name: "xxxs" }
}
const addSexPropery = () => {
if (job.sex) {
delete job.sex
} else {
job.sex = "男"
}
}
return {
age, job, addAge, addSalary, name, list, handleName, addSexPropery, editList
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
.app-page {
display: flex;
background-color: yellow;
}
</style>
4.2 ComputedDemo组件
<template>
<div class="computed-page">
<h3>ComputedDemo page</h3>
<p>父组件传过来的<span style="color:red;font-weight:700">薪资:{{ job.salary }}k</span></p>
<button @click="job.salary++">尝试修改父组件传过来的salary</button><br>
<slot name="slot1"></slot>
<p>哈哈哈</p>
<slot name="slot2"></slot>
<br>
<input type="text" v-model="firstName">
<input type="text" v-model="lastName">
<p>全名:{{fullName}}</p>
</div>
</template>
<script>
import { ref, reactive, computed, watch, readonly } from 'vue'
export default {
name: 'ComputedDemo',
props: {
job: Object
},
created () {
console.log("created")
},
mounted () {
console.log("mounted")
},
setup (props, context) {
console.log("ComputedDemo---setup", props.job)
readonly(props.job)
const name = ref("bwf")
context.emit("handleName", name)
const firstName = ref("")
const lastName = ref("")
const fullName = computed(() => {
console.log('computed', firstName.value);
return firstName.value + '---' + lastName.value;
})
return {
firstName, lastName, fullName
}
}
}
</script>
<style scoped>
.computed-page {
background-color: palegreen;
}
</style>
4.3 WatchDemo组件
<template>
<div class="watch-page">
<h3>WatchDemo Page</h3>
<button @click="sum++">点我sum++</button>
<p>msg:{{msg}}</p>
<button @click="msg+='@'">点我msg++</button>
<p>person---name:{{name}}---age:{{age}}---salary:{{job.salary}}</p>
<button @click="age++">点我修改person.age</button>
<button @click="job.salary++">点我修改person.job.salary</button>
<button @click="name+='@'">点我修改person.name</button>
<child-child></child-child>
</div>
</template>
<script>
import { ref, reactive, computed, watch, toRefs, watchEffect } from 'vue'
import ChildChild from './ChildChild'
export default {
name: "WatchDemo",
components: {
ChildChild
},
setup (props) {
console.log("WatchDemo---setup")
let sum = ref(0);
let msg = ref("你好");
let person = reactive({
name: "bwf",
age: 16,
job: {
salary: 9
}
})
/**
* 可以开启多组监听
*
*/
watch([sum, msg], (newValue, oldValue) => {
console.log('watch---sum', newValue, oldValue) // [sum, msg]
}, { immediate: true })
/**
* 监听reactive定义的对象的全部属性 自动 deep:true 而且oldValue有误
* 如下 监视 person对象任何属性改变,都会执行 watchEffect的回调
*/
watch(person, (newValue, oldValue) => {
// console.log('watch---person', newValue, oldValue)
}, { deep: false })
/**
* 监听reactive定义的对象中某个属性
* 如下 只监视 person对象的name属性,然后做一些逻辑处理
*/
watch(() => person.name, (newValue, oldValue) => {
// console.log('watch---person.name', newValue, oldValue)
})
/**
* 自动 immediate:true 类似computed, 但不接收返回值
* watchEffect用到响应的某个属性 就监视那个属性,并读取最新的值
* 如下 person.name, person.age 发生改变都会执行watchEffect的回调
*/
watchEffect(() => {
console.log('watchEffect', person.name, person.age);
})
return {
sum,
msg,
...toRefs(person)
}
}
};
</script>
<style scoped>
.watch-page {
background-color: pink;
}
</style>
4.4 ChildChild组件
<template>
<div>
<h4>ChildChild page</h4>
<div>app祖父组件通过provide传递过来数据,
<p>工作信息:地点:{{appData.address}}, <span style="color:red;font-weight:700">薪资:{{appData.salary}}k</span> </p>
</div>
</div>
</template>
<script>
import { inject } from 'vue'
export default {
name: "ChildChild",
setup (props) {
console.log('ChildChild---setup');
const appData = inject('appData');
console.log('app祖父组件通过provide传递过来数据', appData);
return {
appData
}
}
};
</script>
<style scoped>
</style>