1. 更快的性能 (提升1.2到2倍)
2. 组合API (最大的亮点setup)
3. Fragment, teleport, Suspense
4. 自定义渲染API
5. 更好的支持TS
6. 支持tree-shaking
1. webpack
git clone https://github.com/vuejs/vue-next-webpack-preview.git
cd projectName
npm install
npm run dev
2. Vue-cli
npm install -g @vue/cli
vue create projectName
cd projectName
vue add Vue-next
npm run serve
3. Vite (比webpack更快的,vue团队的脚手架)
npm install -g create-vite-app
create-vite-app projectName
npm install (or `yarn`)
npm run dev (or `yarn dev`)
1. diff算法 (附录1) (https://vue-next-template-explorer.netlify.app/)
2.0 全量比较DOM
3.0 新增加了静态标记(PatchFlag)
在与上次虚拟节点进行比较的时候,只对比带patch flag的节点
并且可以通过flag的信息得知当前节点要对比的具体内容
2. hoistStatic 静态提升(附录2)
2.0中无论元素是否参与更新,每次都会重新创建,然后就渲染
3.0 对不参与更新的元素,会做静态提升,只会创建一次,在渲染的时候复用
3. cacheHandlers 事件侦听器缓存 (附录3)
默认情况下onClick会被视为动态绑定,所以每次都会去追踪它的变化
但是因为是同一个函数,所以没有追踪变化,直接缓存起来复用即可
4.ssr
当有大量静态内容时,这些内容会被当做纯字符串推进一个buffer里面
即使存在动态的绑定,会通过模板插值嵌入进去,这样会比通过虚拟Dom渲染快很多
当静态内容大到一定量级的时候,会用createStaticVNode方法在客户端去生成一个static node 这些node,会被直接innerHtml,不需要创建对象,然后根据对象渲染
// 数据和处理办法都被 methods watch computed等分隔开
export default {
data () {
return {
// 购物车相关的数据定义
shopCartList: [],
...
// 用户相关
user: {
}
...
}
},
methods: {
// 购物车逻辑
addShopCart () {
},
deleteShopCart () {
},
// 用户相关的逻辑
checkUser () {
},
loginOutUser () {
}
},
watch: {
// 购物车相关
shopCartList () {
},
// 用户相关的逻辑
user () {
},
},
computed: {
// 购物车相关
shopCartList () {
},
// 用户相关的逻辑
user () {
},
}
}
<!--注意点:-->
<!--1. setup 必须return出去-->
<!--2. 在内部不能使用this-->
import {
ref } from 'vue'
export default {
// 组合API入口函数
setup () {
// 定义user,shopCartList相关的data 更新之后 Vue会自动更新UI
let userName = ref('')
let shopCartCount = ref(0)
const updateUserName = (val) => {
userName.value = val
}
const addShopCartCount = () => {
shopCartCount.value += 1
}
return {
userName,
shopCartCount,
updateUserName,
addShopCartCount
}
}
}
// 更加复杂的业务处理 可以将单独的业务从.vue文件中分离出来
import shopCart from './setup/shopCart'
import user from './setup/user'
export default {
// 组合API入口函数
setup () {
// 定义user,shopCartList相关的data 更新之后 Vue会自动更新UI
let {
userName, updateUserName} = user()
let {
shopCartCount , addShopCartCount} = shopCart()
return {
userName,
shopCartCount,
updateUserName,
addShopCartCount
}
}
}
// user.js
import {
ref } from 'vue'
function user () {
let userName = ref('')
const updateUserName = (val) => {
userName.value = val
}
return {
userName,
updateUserName
}
}
export default user
// shopCart.js
import {
ref } from 'vue'
function shopCart () {
let shopCartCount = ref(0)
const addShopCartCount = () => {
shopCartCount.value += 1
}
return {
shopCartCount , addShopCartCount }
}
export default shopCart
beforCreated 和 created 之间
beforCreated 组件刚刚创建出来 组件的data和methods还没有初始化好
setup 是无法使用data和methods 在其中的this为undefined
created 组件刚刚创建出来,组件data和methods刚刚初始化好
1. setup中的this为undefined
2. setup只能是同步不能是异步
1. reactive是什么 监听复杂类型变量
- reacttive是vue3中提供的实现响应式数据的方法
- 在vue2中是通过defineProperty来实现的
在vue3中响应式数据是通过ES6的Proxy来实现的
2. reactive注意点
- 参数必须是对象(json/arr)
- 默认情况下修改对象不会触发界面更新
- 想更新必修重新赋值
3. ref 监听简单类型
- ref和reactive一样都是实现响应式数据的方法
- reactive必须传一个对象,所以一个变量想实现响应式就比较麻烦,所以ref帮助我们实现变量的响应式
- ref本职上还是一个reactive,系统会自动根据我们给ref传入的值将他转换
ref(xx) => reactive({value:xxx})
4. ref注意点
在vue中使用ref的值不需要value获取
js中使用或者赋值必须使用ref.value
5. isRef , isReactive 判断是不是ref或者isReactive创建的
6. shallowRef, shallowReactive 非递归监听
- ref 和 reactive 都是递监听的 存在的问题是性能消耗严重(每一层都会包装成proxy)所以可以使用shallow + xx 来表示非递归监听的方法
- * 通过shallowReactve创建的数据,只会监听第一层的变化
- * 通过shallowRef创建的数据,那么Vue监听的是.value的变化,而不是第一层的变化
- * 使用场景: 一般使用ref和reactive就可以,只有数据量比较大使用shallowRef和shallowReactive
7. shallowRef本质
ref 本质
ref(xx) -> reactive({value:xx})
shallowRef本质
shallowRef(xx) -> shallowReactive({value:xx})
* 通过shallowRef创建的数据,那么Vue监听的是.value的变化
8. triggerRef会根据传入的值,更新一遍数据
- * v3只提供了triggerRef方法,并没有提供triggerReactive方法。
那么reactive类型的数据,无法主动触发界面更新
9. toRow是什么
用来获取ref或者reactive的原始数据
let obj = {name: 'jack', age: 18}
let state = reactive(obj)
let obj2 = toRaw(state)
obj2 === obj
如果是ref定义的 想通过toRaw处理得到原始值 要获取的是 。value的值 因为经过vue处理之后, .value中保存的才是当初创建是传入的那个原始数据
let count = 1
let state = ref(count)
let count2 = toRaw(count.value)
count === count2
10. markRaw是什么
如果你希望一个数据永远都不要被监听就可以使用markRaw
let obj = {name: 'jack', age: 18}
let obj = markRaw(obj)
let state = reactive(obj)
修改state的值将不会引发页面相应,因为MarkRaw已经将obj变成永远无法监听的响应式数据
11. toRef是什么
想对一个对象中的某个属性进行响应式处理
let obj = { name: 'jack'}
let status = toRef(obj, 'name')
const myFun = () => {
status.value = 'jobs'
console.log(obj) // {name: "jobs"}
console.log(status)
// ObjectRefImpl {
// __v_isRef: true
// _key: "name"
// _object: {name: "jobs"}
// value: "jobs"
// }
}
ref和toRef区别
ref本质是复制,修改响应式数据不会影响以前的数据
toRef本质是引用,修改响应式数据的值是会影响以前的数据
ref数据变化,界面就会自动更新
toRef数据变化,界面不会自动更新
应用场景
如果想让响应式数据和以前数据关联起来,并且更新响应式数据之后还不想更新UI,那么就可以使用toRef
let obj = {name: 'jack', age: 18}
let name = toRef(obj, 'name')
12. toRefs是什么
想把某一个对象中的多个属性变成响应式对象
let obj = { name: 'jack', age: 18}
let status = toRefs(obj)
const myFun = () => {
status.name.value = 'jobs'
status.age.value = 20
console.log(obj) // {name: "jobs", age: 20}
console.log(status) // 附件4
}
13. customRef是什么
自定义ref方法
function myRef (val) {
return customRef((track, trigger) => {
return {
get () {
track() // 告诉我们Vue 这个数据是需要追踪变化
console.log('get', val)
return val
},
set (newValue) {
console.log('set', newValue)
val = newValue
trigger() // 告诉我们Vue 触发我们界面更新
}
}
})
}
14. 为什么要使用customRef
serup中不能使用async 但是我们确实想在其中使用就可以将异步代码写在自定义的方法里面
let age = myRef('../public/data.json')
function myRef (val) {
return customRef((track, trigger) => {
fetch(val).then(res => {
return res.json()
}).then(data => {
console.log(data)
val = data
trigger()
})
return {
get () {
track() // 告诉我们Vue 这个数据是需要追踪变化
console.log('get', val)
return val
},
set (newValue) {
console.log('set', newValue)
val = newValue
trigger() // 告诉我们Vue 触发我们界面更新
}
}
})
}
15. ref 与 原来2.0 ref有什么区别
在setup中不能使用$refs, 那我们如何使用ref标记的DOM
setup () {
const box = ref(null)
onMounted(() => {
console.log('onMounted', box.value)
})
console.log(box.value)
return {
box
}
}
16. setup中需要使用生命周期 从vue中引入, 上述例子中使用了 onMounted
import { ref, onMounted } from 'vue'
setup () {
const box = ref(null)
onMounted(() => {
console.log('onMounted', box.value)
})
return {
box
}
}
17. readonly
通过readonly创建只读的数据,而且是递归只读!
18 shallowReadobly
通过shallowReadobly创建只有第一层是只读的数据, 不是递归只读!
19. isReadonly
是不是通过readonly创建只读的数据
原先版本
new Vue({
el: '#root'
})
3.0
Vue.createApp(Counter).mount('#root')
2.x 版本中在一个元素上同时使用 v-if 和 v-for 时,v-for 会优先作用
3.x 版本中 v-if 总是优先于 v-for 生效。
由于语法上存在歧义,建议避免在同一元素上同时使用两者。
比起在模板层面管理相关逻辑,更好的办法是通过创建计算属性筛选出列表,并以此创建可见元素。
只有3.0有 多事件并不是每次执行一个,而是依次执行的
<!-- 这两个 one() 和 two() 将执行按钮点击事件 -->
<button @click="one($event), two($event)">
Submit
</button>
one(event) {
// first handler logic...
console.log('one')
this.counter++
},
two(event) {
// second handler logic...
console.log('two')
this.counter++
},
three(event) {
// second handler logic...
console.log('three')
this.counter++
}
const Mixin = {
data() {
return {
user: {
name: 'Jack',
id: 1
}
}
}
}
const CompA = {
mixins: [Mixin],
data() {
return {
user: {
id: 2
}
}
}
}
2.0
{
user: {
id: 2,
name: 'Jack'
}
}
3.0
{
user: {
id: 2
}
}
组件中不需要再外层加一个div
场景:Modal 虽然控制在组件里面但是样式实际上是在body的末尾 现在可以使用teleport方便的创建
// Vue 2 渲染函数示例
export default {
render(h) {
return h('div')
}
}
h 是 createElement 别名
// Vue 3 渲染函数示例
import {
h } from 'vue'
export default {
render() {
return h('div')
}
}
// 这里的flag值就是1 只有text
<div>
<p>你好</p>
<p>你好</p>
<p>你好</p>
<p>{
{
msg}}</p>
</div>
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", null, [
_createVNode("p", null, "你好"),
_createVNode("p", null, "你好"),
_createVNode("p", null, "你好"),
_createVNode("p", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)
]))
}
// 这里的flag值就是3 text和class
<div>
<p>你好</p>
<p>你好</p>
<p>你好</p>
<p>{
{
msg}}</p>
<p :class="{'isNew': msg='123'}">{
{
msg}}</p>
</div>
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", null, [
_createVNode("p", null, "你好"),
_createVNode("p", null, "你好"),
_createVNode("p", null, "你好"),
_createVNode("p", null, _toDisplayString(_ctx.msg), 1 /* TEXT */),
_createVNode("p", {
class: {
'isNew': _ctx.msg='123'}
}, _toDisplayString(_ctx.msg), 3 /* TEXT, CLASS */)
]))
}
2,0 每次都会createVnode 3,0会把不需要的缓存下来
const _hoisted_1 = /*#__PURE__*/_createVNode("p", null, "你好", -1 /* HOISTED */)
const _hoisted_2 = /*#__PURE__*/_createVNode("p", null, "你好", -1 /* HOISTED */)
const _hoisted_3 = /*#__PURE__*/_createVNode("p", null, "你好", -1 /* HOISTED */)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", null, [
_hoisted_1,
_hoisted_2,
_hoisted_3,
_createVNode("p", null, _toDisplayString(_ctx.msg), 1 /* TEXT */),
_createVNode("p", {
class: {
'isNew': _ctx.msg='123'}
}, _toDisplayString(_ctx.msg), 3 /* TEXT, CLASS */)
]))
}
<div>
<div @click="onClick"></div>
</div>
// 没有开启缓存
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", null, [
_createVNode("div", {
onClick: _ctx.onClick }, null, 8 /* PROPS */, ["onClick"])
]))
}
// 开启之后
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", null, [
_createVNode("div", {
onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.onClick(...args)))
})
]))
}