這幾天,陸續學習瞭解了關於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
文件
安裝 vue-cli
$ 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。