这几天,陆续学习了解了关于vue-next(Vue 3.0)的一些新特性,尤为是新的Composition API
的用法。这套新的API中最重要、最核心的部分,恐怕就是实现响应式功能的这一块了。并且,这套响应式API不只能够在vue-next
环境下使用,也能够独立使用。javascript
笔者在阅读源码看到,vue-next
已所有由TypeScript
构建,看来 ts 必学技能。接下来带你了解vue-next。html
vue-next
计划并已实现的主要架构改进和新功能:vue
运行时(Runtime)的更新主要体如今如下几个方面:java
最后,还有一些 2.x 的功能还没有移植过来,以下:node
==目前不支持IE11==react
vue-next(Vue 3.0) 的源码虽然发布了,可是预计最先也须要等到 2020 年第一季度才有可能发布 3.0 正式版。git
代码仓库中有个 packages 目录,里面主要是 vue-next
的相关源码功能实现,具体内容以下所示。github
├── packages
│ ├── compiler-core # 全部平台的编译器
│ ├── compiler-dom # 针对浏览器而写的编译器
│ ├── reactivity # 数据响应式系统
│ ├── runtime-core # 虚拟 DOM 渲染器 ,Vue 组件和 Vue 的各类API
│ ├── runtime-dom # 针对浏览器的 runtime。其功能包括处理原生 DOM API、DOM 事件和 DOM 属性等。
│ ├── runtime-test # 专门为测试写的runtime
│ ├── server-renderer # 用于SSR
│ ├── shared # 帮助方法
│ ├── template-explorer
│ └── vue # 构建vue runtime + compiler
复制代码
compiler-core:平台无关的编译器,它既包含可扩展的基础功能,也包含全部平台无关的插件。暴露了 AST 和 baseCompile 相关的 API,它能把一个字符串变成一棵 ASTchrome
compiler-dom:基于compiler-core封装针对浏览器的compilervue-cli
runtime-core:与平台无关的运行时环境。支持实现的功能有虚拟 DOM 渲染器、Vue 组件和 Vue 的各类API, 能够用来自定义 renderer ,vue2中也有
runtime-dom:针对浏览器的 runtime。其功能包括处理原生 DOM API、DOM 事件和 DOM 属性等, 暴露了重要的render和createApp方法
const { render, createApp } = createRenderer<Node, Element>({
patchProp,
...nodeOps
})
export { render, createApp }
复制代码
runtime-test:一个专门为了测试而写的轻量级 runtime。好比对外暴露了renderToString方法,在此感慨和react愈来愈像了
server-renderer:用于 SSR,还没有实现。
shared:没有暴露任何 API,主要包含了一些平台无关的内部帮助方法。
vue:「完整」版本,引用了上面提到的 runtime 和 compiler目录。入口文件代码以下
'use strict'
if (process.env.NODE_ENV === 'production') {
module.exports = require('./dist/vue.cjs.prod.js')
} else {
module.exports = require('./dist/vue.cjs.js')
}
复制代码
因此想阅读源码,仍是要看构建流程,这个和vue2也是一致的
复制代码
这个原理老生常谈了,就是拦截对象,给对象的属性增长set
和 get
方法,由于核心是defineProperty
因此还须要对数组的方法进行拦截
function observer(target){
// 若是不是对象数据类型直接返回便可
if(typeof target !== 'object'){
return target
}
// 从新定义key
for(let key in target){
defineReactive(target,key,target[key])
}
}
function update(){
console.log('update view')
}
function defineReactive(obj,key,value){
observer(value); // 有可能对象类型是多层,递归劫持
Object.defineProperty(obj,key,{
get(){
// 在get 方法中收集依赖
return value
},
set(newVal){
if(newVal !== value){
observer(value);
update(); // 在set方法中触发更新
}
}
})
}
const obj = {name:'zhuanzhuan'}
observer(obj);
obj.name = 'new-name';
复制代码
输出:
update view
复制代码
const oldProtoMehtods = Array.prototype
const proto = Object.create(oldProtoMehtods)
function update(){
console.log('update view')
}
function defineReactive(obj,key,value){
observer(value) // 有可能对象类型是多层,递归劫持
Object.defineProperty(obj,key,{
get(){
// 在get 方法中收集依赖
return value
},
set(newVal){
if(newVal !== value){
observer(value)
update() // 在set方法中触发更新
}
}
})
}
['push','pop','shift','unshift'].forEach(method=>{
Object.defineProperty(proto, method,{
get(){
update()
return oldProtoMehtods[method]
}
})
})
function observer(target){
if(typeof target !== 'object'){
return target
}
// 若是不是对象数据类型直接返回便可
if(Array.isArray(target)){
Object.setPrototypeOf(target, proto)
// 给数组中的每一项进行observr
for(let i = 0 ; i < target.length; i++){
observer(target[i])
}
return
}
// 从新定义key
for(let key in target){
defineReactive(target, key, target[key])
}
}
let obj = {hobby:[{name:'zhuanzhuan'}]}
observer(obj)
// 使用['push','pop','shift','unshift'] 方法,更改数组会触发视图更新
obj.hobby.push('转转')
// 更改数组中的对象也会触发视图更新
obj.hobby[0].name = 'new-name'
console.log(obj.hobby)
复制代码
输出:
update view
update view
[ { name: [Getter/Setter] }, '转转' ]
复制代码
Object.defineProperty缺点:
不管是阅读这篇文章,仍是阅读 vue-next
响应式模块的源码,首先有两个知识点是必备的:
let data = [1,2,3]
let p = new Proxy(data, {
get(target, key) {
console.log('获取值:', key)
return target[key]
},
set(target, key, value) {
console.log('修改值:', key, value)
target[key] = value
return true
}
})
p.push(4)
复制代码
输出:
获取值: push
获取值: length
修改值: 3 4
修改值: length 4
复制代码
比defineproperty
优秀的 就是数组和对象均可以直接触发getter
和setter
, 可是数组会触发两次,由于获取push
和修改length
的时候也会触发
Proxy 取代 deineProperty 除了性能更高之外,还有如下缺陷,也是为啥会有delete的缘由 :
let data = [1,2,3]
let p = new Proxy(data, {
get(target, key) {
console.log('获取值:', key)
return Reflect.get(target,key)
},
set(target, key, value) {
console.log('修改值:', key, value)
return Reflect.set(target,key, value)
}
})
p.push(4)
复制代码
输出:
获取值: push
获取值: length
修改值: 3 4
修改值: length 4
复制代码
let data = {name:{ title:'zhuanzhuan'}}
let p = new Proxy(data, {
get(target, key) {
console.log('获取值:', key)
return Reflect.get(target,key)
},
set(target, key, value) {
console.log('修改值:', key, value)
return Reflect.set(target,key, value)
}
})
p.name.title = 'xx'
复制代码
输出:
获取值: name
复制代码
以后会带你看下vue-next
是怎么解决的。
clone 项目
$ git clone https://github.com/vuejs/vue-next.git
复制代码
编辑文件
$ npm run dev
复制代码
拷贝文件
运行上面命令后,就会生成 [项目根路径]/packages/vue/dist/vue.global.js
文件
$ npm install -g @vue/cli
# OR
$ yarn global add @vue/cli
复制代码
$ vue create my-project
# OR
$ vue ui
复制代码
composition-api
体验 vue-next
新特性$ npm install @vue/composition-api --save
# OR
$ yarn add @vue/composition-api
复制代码
@vue/composition-api
提供的能力前,必须先经过 Vue.use()
进行安装import Vue from 'vue'
import VueCompositionApi from '@vue/composition-api'
Vue.use(VueCompositionApi)
复制代码
安装插件后,您就可使用新的 Composition API 来开发组件了。
直接拷贝下面代码,去运行看效果吧。推荐使用高版本的chrome浏览器,记得打开F12调试工具哦!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://s1.zhuanstatic.com/common/js/vue-next-3.0.0-alpha.0.js"></script>
</head>
<body>
<div id='app'></div>
</body>
<script> const { createApp, reactive, computed, effect } = Vue; const RootComponent = { template: ` <button @click="increment"> {{ state.name }}今年{{state.age}}岁了,乘以2是{{state.double}} </button> `, setup() { const state = reactive({ name: '转转', age: 3, double: computed(() => state.age * 2) }) effect(() => { console.log(`effect 触发了! - ${state.name}今年${state.age}岁了,乘以2是${state.double}`) }) function increment() { state.age++ } return { state, increment } } } createApp().mount(RootComponent, '#app') </script>
</html>
复制代码
这个reactive和react-hooks愈来愈像了, 你们能够去Composition API RFC这里看细节。
template
和以前同样,一样vue-next
也支持手写render
的写法,template
和render
同时存在的状况,优先render
。
setup
选项是新增的主要变更,顾名思义,setup
函数会在组件挂载前(beforeCreate
和created
生命周期之间)运行一次,相似组件初始化的做用,setup
须要返回一个对象或者函数。返回对象会被赋值给组件实例的renderContext
,在组件的模板做用域能够被访问到,相似data
的返回值。返回函数会被当作是组件的render
。具体能够细看文档。
reactive
的做用是将对象包装成响应式对象,经过Proxy
代理后的对象。
上面的计数器的例子,在组件的setup
函数中,建立了一个响应式对象state
包含一个age
属性。而后建立了一个increment
递增的函数,最后将state
和increment
返回给做用域,这样template
里的button
按钮就能访问到increment
函数绑定到点击的回调,age
。咱们点击按钮,按钮上的数值就能跟着递增。
相信你们已经对 vue-next(Vue 3.0) 有了初步认识,而且已经成功运行尝鲜代码了吧。
下一章vue-next(Vue 3.0)之 小试牛刀
继续带你掌握 vue-next
函数式的API。