概要
vue3.0 相容 vue2.x 版,强化了主要核心理念单向存取基本原理和表面积大小不一,因此更为亲善的相容 ts 句法。
工程建设建立
vue-cil 建立
优先选择 vue3.0 聚合工程建设项目方可
## 查阅@vue/cli版,保证@vue/cli版在4.5.0以内
vue –version
## 加装或是升级换代你的@vue/cli
npm install -g @vue/cli
## 建立vue create vue_test## 开启
cd vue_test
npm run serve
采用 vite 建立
Vite 是一个 web 合作开发构筑辅助工具,由于其原生植物 ES 组件引入形式,能同时实现流星般冷伺服器启动。(这儿不详尽科学研究)
透过在终端产品中运转下列指示,能采用 Vite 加速构筑 Vue 工程建设项目。
# npm 6.x$ npm init vite@latest <project-name> –template vue# npm 7+,须要加之附加的双短纵线
$ npm init vite@latest <project-name> — –template vue
$ cd<project-name>
$ npm install
$ npm run dev
对照三种形式建立工程建设项目
vite 竞争优势如下表所示:
合作开发自然环境中,无须装箱操作方式,可加速的UAC。高性能加速的热空载(HMR)。或是说的按需校对,无须等候整座应用领域校对顺利完成。 现代 webpack 校对:每天继续执行校对时,单厢透过出口处 entry 先去找出各路由器,再去读取每一路由器各别的组件,接着会进行装箱正式成为 bundle.js 文档,最终才通告伺服器热预览。因此换言之就是等大部份文档读取准备就绪后才去图形预览网页的==》较快
vite 校对:与现代构筑不同的是,vite 会先准备好伺服器预览,再去找出出口处文档接着再动态的找出须要读取的路由器去校对那个路由器下的组件,类似于按需读取,总体表面积较小且预览更快。
vue3.0 优点
性能方面
装箱大小不一减少
初次图形快 , 预览图形快
内存减少
源码方面
移除一些冷门 API,比如 filter、inline-template 等,表面积减少引入 tree-shaking 减少装箱表面积(透过校对阶段的静态分析,找出没有引入的组件并打上标记,因此移除)采用 Proxy 代替 defineProperty 同时实现响应式。Vue3 能更好的支持 TypeScriptComposition API(组合 API)能按需采用,多余勾子配置不用再次装箱。vue3.0 响应式基本原理
vue2.x 透过 Object.defineProperty()同时实现的响应式基本原理存在下列问题
新增属性、删除属性, 监听不到变化从而不是响应式数据,网页也不会改变直接透过下标修改数组也监听不到。 对此 vue3.0 解决了这些问题proxy 概要
vue 响应式基本原理本质就是 ES6 新句法 proxy 同时实现的。 Proxy 能理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先透过这层拦截,因此提供了一种机制,能对外界的访问进行过滤和改写. 接收两个参数,var proxy = new Proxy(target, handler); target 参数表示所要拦截的目标对象,handler 参数也是一个对象,用来定制拦截行为。
// 源对象
let obj = {
name: haha,
age: 18,
}
// handler作为拦截配置对象
const p = new Proxy(obj, {
// 读取操作方式
get(target, propName) {
// get方法接收2个参数 target指源对象,就是obj,propName就是你当前操作方式的属性
// 返回当前读取的值
return target[propName]
},
// 修改操作方式,新增属性操作方式时单厢触发
set(target, propName, value) {
// set方法接收3个参数 target指源对象,前面2个与上相同,第三个参数是修改的值
// 通告原对象修改数据 target[propName] = value
},
//删除操作方式
deleteProperty(target, propName) {
return delete target[propName]
},
})
// 以内 方可同时实现对象的增删改查的监听,同时实现数据的相应式
Reflect 概要
Reflect 对象与 Proxy 对象一样,也是 ES6 为了操作方式对象而提供的新 API。具有反射的意思。 将 Object 对象的一些明显属于语言内部的方法(比如 Object.defineProperty),放到 Reflect 对象上。现阶段,某些方法同时在 Object 和 Reflect 对象上部署,未来的新方法将只部署在 Reflect 对象上。也就是说,从 Reflect 对象上能拿到语言内部的方法。
修改某些 Object 方法的返回结果,让其变得更合理,会有一个布尔返回值,易于捕捉错误(js 单线程,报错会阻塞进程)
// 老写法
try {
Object.defineProperty(target, property, attributes)
// success
} catch (e) {
// failure}
// 新写法
if (Reflect.defineProperty(target, property, attributes)) {
// success
} else {
// failure
}
let obj = {
name: haha,
age: 18,
}
// 三个静态方法:
Reflect.get(obj, name) // 返回 haha 读取对象的某个属性的值
Reflect.set(obj, name, 小明) // 返回布尔 修改某个属性的值Reflect.deleteProperty(obj, name) // 返回布尔 删除某个属性
const p = new Proxy(obj, {
//有人读取p的某个属性时调用
get(target, propName) {
return Reflect.get(target, propName)
},
//有人修改p的某个属性、或给p追加某个属性时调用
set(target, propName, value) {
Reflect.set(target, propName, value)
},
//有人删除p的某个属性时调用
deleteProperty(target, propName) {
return Reflect.deleteProperty(target, propName)
},
})
生命周期如是说
透过官网生命周期图能看出Vue3.0中能继续采用Vue2.x中的生命周期钩子,但有有2个钩子发生了改变 – beforeDestroy改名为beforeUnmount(卸载前) – destroyed改名为unmounted(卸载) 与vue2不同的是,vue3中是有了el模板后才会去初始化,而vue2中是先created后再去找模板。
分为2种形式的生命周期勾子
组合式 api(Composition API)
setup
它是 vue3 中一个新的配置项,值为一个函数。大部份的组合 api 都要在它里面采用。
采用如是说
使用变量 或是事件 须要把名字 return 出去方可在模板中采用。export default {
setup() {
let name = zhang
function at() {
console.log(1)
}
return {
name,
at,
}
},
}
setup 函数的三种返回值,一种就是上面常规返回一个对象,则对象中的属性、方法, 在模板中均能直接采用,还有一种就是返回一个函数// 若返回一个图形函数:则能自定义图形内容import { h } from vue
export default {
setup() {
return () => h(h1, 你好)
},
}
注意 vue3 虽然能相容 vue2,但是尽量不能混合采用。
Vue2.x 配置(data、methos、computed…)中能访问到 setup 中的属性、方法
但是由于 setup 中没有 this,因此 setup 中没办法读取 Vue2.x 配置中的数据和方法如果有重名, setup 优先setup 的注意点
setup 继续执行的时机是最早的,在 beforeCreate 之前继续执行,因此此时 this 是 undefined参数问题 setup 接收 2 个参数props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
context:上下文对象
– attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于“`this.$attrs“`。
– slots: 收到的插槽内容, 相当于 “`this.$slots“`。
– emit: 分发自定义事件的函数, 相当于“`this.$emit“`
// 在子组件中
export default {
props: [msg, school],
emits: [hello],
setup(props, context) {
// props接收props配置项中的相应式数据{msg:,school:}// context相当于上下文对象,里面有三个值attrs,slots,emit
//方法function test() {
// 调用父组件方法
context.emit(hello, 666)
}
return {
test,
}
},
}
ref 函数
作用: 定义一个响应式的数据(主要针对此基础类型数据) 方法:引入 ref 函数,const xxx = ref(initValue) 模板中读取数据: 不须要.value,直接:<div>{{xxx}}</div>
处理基本数据类型
RefImpl 对象中.value 是此基础类型时,用的是 Object.defineProperty 透过 get 和 set 同时实现的响应式数据
import { ref } from vue
export default {
setup() {
let name = ref(张三)
function change() {
console.log(name, name)
//ref加工后聚合一个 RefImpl引用对象,该对象的原型对象上能发现,底层其实还是Object.defineProperty透过 // get 和set同时实现的响应式数据
// 因此改变基本数据须要用到RefImpl引用对象中的value属性 name.value = 小明
}
return {
name,
change,
}
},
}
处理对象类型
RefImpl 对象中.value 是对象时候,用的是 proxy 代理对象同时实现的响应式数据
import { ref } from vue
export default {
setup() {
let obj = ref({
name: 小明,
age: 20,
})
function change() {
obj.value.name = 小工
obj.value.age++
console.log(obj.value)
//能发现是一个Proxy 对象,其本质其实调用的是“`reactive“`函数同时实现Proxy代理响应式对象
}
return {
obj,
change,
}
},
}
reactive 函数
作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数) 方法:const x= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy 的实例对象,简称 proxy 对象) 特点:能同时实现数组、深层对象的响应式数据,这是 vue2.0 中无法同时实现的,底层基于 Proxy
export default {
setup() {
let obj = reactive({
name: 小明,
age: 20,
})
function change() {
console.log(obj, obj)
//能发现obj此时就是一个Proxy的实例对象能直接修改对象内部属性 obj.name = 小三
obj.age++
}
return {
obj,
change,
}
},
}
总结 ref 和 reactive
从定义数据角度对照
ref 用来定义:基本类型数据。
reactive 用来定义:对象(或数组)类型数据。 备注:ref 也能用来定义对象(或数组)类型数据, 它内部会自动透过reactive转为代理对象。
从基本原理角度对照
ref 透过Object.defineProperty()的get与set来同时实现响应式(数据劫持)。
reactive 透过采用 Proxy 来同时实现响应式(数据劫持), 并透过 Reflect 操作方式源对象内部的数据。计算属性与监视
computed函数
与Vue2.x中computed配置功能一致
// 1.直接读取(简写)
import { reactive,computed } from vue
export default {
setup () {
let obj = reactive({
name: haha,
age: 18
})
let ages = computed(() => {
return obj.age + 1
})
return {
ages
}
},
}
// 1.计算属性修改情况(完整版)export default {
setup () {
let obj = reactive({
name: haha,
age: 18
})
let fullName = computed({
get () {
return obj.age + 1
},
set (value) {
obj.age = value
}
})
return {
obj,
fullName
}
},
}
watch函数
watch接收三个参数 监听的对象,监听的回调和监视的配置参数
watch(被监听的对象,()=>{},{immediate:立即监听,deep:深度监听})
export default {
setup () {
let age = ref(18)
let name = ref(小明)
let obj = reactive({
money: 100
})
function change () {
obj.money += 100
age.value++
name.value += –
}
//情况一:监视ref所定义的一个响应式数据
watch(age, (newValue, oldValue) => {
console.log(age, newValue, oldValue)
},{immediate:true})
//情况二:监视ref所定义的多个响应式数据
watch([age, name], (newValue, oldValue) => {
console.log(age-name, newValue, oldValue)//也是数组形式返回
})
// 情况三:监视reactive所定义的一个响应式数据的全部属性
watch(obj, (newValue, oldValue) => {
})
// 情况四:监视reactive所定义的一个响应式数据中的某个属性
watch(() => obj.money, (newValue, oldValue) => {
// [()=>person.name,()=>person.age]多参数时候也须要数组
// 监听的参数须要以函数的形式返回才能监听到
//当监听的参数是深层对象,须要配置deep为true
})
return {
obj,
name,
age,
change
}
},
}
watchEffect函数
不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。 watchEffect有点像computed: – 但computed注重的计算出来的值(回调函数的返回值),因此必须要写返回值。 – 而watchEffect更注重的是过程(回调函数的函数体),因此不用写返回值。
watchEffect(() => {
let a = obj.money
console.log(改变了)
//默认是立即监听,当采用的数据改变时,就会触发。能用于关联数据改变回调方法等场景
})
自定义hook函数
什么是hook: 本质是一个函数,把setup函数中采用的Composition API进行了封装。 特点:类似于vue2.x中的mixin 竞争优势: 复用代码, 让setup中的逻辑更清楚易懂。
toRef和toRefs
建立一个 ref 对象,其value值指向另一个对象中的某个属性。 用法:const name = toRef(obj,name) 要将响应式对象中的某个属性单独提供给外部采用时,便于简写。
// toRef聚合一个对象,本质还是保持对其源对象属性的响应式连接。export default {
setup () {
let obj = reactive({
take: {
money: 100,
}
})
function change () {
obj.take.money += 100
}
return {
money: toRef(obj.take, money),
//注意:此处不能采用ref(obj.take, money),一般情况下网页虽然效果一样,但是本质上修改的源对象不是reactive里面 // 的obj,而是ref出来的一个新对象,不能使源obj响应式修改。
change
}
},
}
toRefs的采用与toRef一样,不过是处理整座对象的大部份属性,批量建立多个 ref 对象
<template>
<div>
{{money}}
{{age}}
<button @click=”change”>改变 </button>
</div>
</template>
<script>
import { reactive, toRefs } from vue
export default {
setup () {
let obj = reactive({
money: 100,
age: 18,
})
function change () {
obj.money += 100
obj.age++
}
console.log(toRefs(obj));//聚合的是一个和obj源对象关联的处理对象集合
return {
…toRefs(obj),
change
}
},
}
</script>
shallowReactive 与 shallowRef
shallowReactive:只处理对象最外层属性的响应式(浅响应式)。 shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。export default {
setup () {
// 只会处理第一层属性
let obj = shallowReactive({
money: 100,
age: 18,
per:{
name:lili
}
})
// 此时不会响应式修改这个x对象 let x = shallowRef({
y:0
})
return {
obj,
x
}
},
}
readonly 与 shallowReadonly
readonly: 让一个响应式数据变为只读的(深只读)。shallowReadonly:让一个响应式数据变为只读的(浅只读)。应用领域场景: 不希望数据被修改时。toRaw 与 markRaw
toRaw 将一个由reactive聚合的响应式对象转为普通对象 采用场景:用于读取响应式对象对应的普通对象,对这个普通对象的大部份操作方式,不会引起网页预览。export default {
setup () {
let obj = reactive({
money: 100,
age: 18,
})
function change () {
let p = toRaw(obj)
//此时的p就是一个不具有响应式的原生植物对象,且只能针对reactive包装后的响应式对象
p.money += 100
p.age++
}
return {
…toRefs(obj),
change
}
},
}
markRaw 标记一个对象,使其永远不会再正式成为响应式对象.有些值不应被设置为响应式的,例如复杂的第三方类库等。当图形具有不可变数据源的大列表时,跳过响应式转换能提高性能export default {
setup () {
let obj = reactive({
money: 100,
age: 18,
person: {}
})
function change () {
// 当定义好的对象后面须要再加入新的属性时
let person = { name: xixi, class: one }
// obj.person = person //此时也是响应式数据,如果当新增的数据对象非常复杂,只要求展示的时候, // 就要求不是响应式对象时 :
obj.person = markRaw(person)
}
return {
obj,
…toRefs(obj),
change
}
},
}
customRef
建立一个自定义的 ref,并对其依赖项跟踪和预览触发进行显式控制。它须要一个工厂函数,该函数接收 track 和 trigger 函数作为参数,因此应该返回一个带有 get 和 set 的对象.
采用自定义 ref 透过 v-model 同时实现 debounce(防抖) 的示例
<template>
<input type=”text” v-model=”keyWord”>
<h3>{{keyWord}}</h3>
</template>
<script>
import {ref,customRef} from vue
export default {
name: App,
setup() {
//自定义一个ref——名为:myRef
function myRef(value,delay){
let timer
return customRef((track,trigger)=>{
return {
get(){
console.log(`有人从myRef这个容器中读取数据了,我把${value}给他了`)
track() //通告Vue追踪value的变化(提前和get商量一下,让他认为这个value是有用的)
return value
},
set(newValue){
console.log(`有人把myRef这个容器中数据改为了:${newValue}`)
clearTimeout(timer)
timer = setTimeout(()=>{
value = newValue
trigger() //通告Vue去重新解析模板
},delay)
},
}
})
}
// let keyWord = ref(hello) //采用Vue提供的ref
let keyWord = myRef(hello,500) //采用程序员自定义的ref
return {keyWord}
}
}
</script>
响应式数据的判断
isRef: 检查一个值是否为一个 ref 对象isReactive: 检查一个对象是否是由 reactive 建立的响应式代理isReadonly: 检查一个对象是否是由 readonly 建立的只读代理isProxy: 检查一个对象是否是由 reactive 或是 readonly 方法建立的代理Composition API 的竞争优势
Options API 存在的问题(vue2)
现代OptionsAPI中,同时实现一个需求,就须要分别在data,methods,computed里修改,当代码量巨大逻辑复杂时,就会造成代码难以维护和查找。
Composition API 的竞争优势(vue3)
能更为优雅的组织的代码,函数。让相关功能的代码更为有序的组织在一起,更为有逻辑性可读性
新的内置组件
Fragment
在Vue2中: 组件必须有一个根标签在Vue3中: 组件能没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中好处: 减少标签层级, 减小内存占用Teleport
Teleport 是一种能够将我们的组件html结构移动到指定位置的技术
须要 prop,必须是有效的查询选择器或 HTMLElement (如果在浏览器自然环境中采用)。指定将在其中移动 内容的目标元素
请注意,这将移动实际的 DOM 节点,而不是被销毁和重新建立,因此它还将保持任何组件实例的活动状态。
<teleport to=”#some-id” />
<teleport to=”.some-class” />
<teleport to=”[data-teleport]” />
<teleport to=”移动位置”>
<div v-if=”isShow” class=”mask”>
<div class=”dialog”>
<h3>我是一个弹窗</h3>
<button @click=”isShow = false”>关闭弹窗</button>
</div>
</div>
</teleport>
其他改动
全局API的转移 将全局的API,即:Vue.xxx调整到应用领域实例(app)上
2.x 全局 API(Vue)3.x 实例 API (app)Vue.config.xxxxapp.config.xxxxVue.config.productionTip移除Vue.componentapp.componentVue.directiveapp.directiveVue.mixinapp.mixinVue.useapp.useVue.prototypeapp.config.globalProperties 其他改动
移除keyCode作为 v-on 的修饰符,同时也无须支持config.keyCodes
移除v-on.native修饰符
父组件中存取事件
vue
子组件中声明自定义事件
vue
移除过滤器(filter)
过滤器虽然这看起来很方便,但它须要一个自定义句法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有同时实现成本!建议用方法调用或计算属性去替换过滤器。