xn-vue3.js | codepen演示javascript
Pre-Alpha. 內測版html
20-04-17 → Beta 公測版vue
20-07-18 → RC (Release Candidate) 候選發佈版java20-09-18 pm 11:28 → Announcing Vue 3.0 :=>
v3.0.0
One Piecereact
This new major version of the framework provides improved performance, smaller bundle sizes, better TypeScript integration, new APIs for tackling large scale use cases, and a solid foundation for long-term future iterations of the framework.git
學不動啦github
git clone https://github.com/vuejs/vue-next.git
cd vue-next # 並安裝依賴
yarn dev # create packages\vue\dist\vue.global.js
複製代碼
<!-- # new packages\vue\index.html -->
<div id="app"></div>
<!-- 引入 vue.global.js -->
<script src="./dist/vue.global.js"></script>
<script> // reactive 響應式 const { createApp, reactive, computed, effect } = Vue // 組件定義 const XnComponent = { template: `<button @click="increment">Count is {{ state.count }}, double is {{ state.double }}</button>`, // 數據處理 setup(){ // 定義響應數據, // data(){return { count: 0 }} const state = reactive({ count: 0, double: computed(() => state.count * 2) }) // 定義方法 function increment(){ state.count++ } effect(() => { // 反作用 console.log('數字修改了', state.count) }) // 返回依賴 return { state, increment, } }, } // 建立app 掛載組件 XnComponent 到 #app createApp().mount(XnComponent, '#app') </script>
複製代碼
let input = document.querySelector('#input')
let span = document.querySelector('#span')
let obj = {}
// 數據劫持
Object.defineProperty(obj, 'text', {
configurable: true,
enumerable: true,
get(){
console.log('獲取值: ')
},
set(newVal){
console.log('更新值: ', newVal)
input.value = newVal
span.innerHTML = newVal
}
})
// 輸入監聽
input.addEventListener('keyup', e => {
obj.text = e.target.value
})
複製代碼
// new Proxy(obj, handler)
/** * Object 類型 */
// let obj = {name: 'xn213'}
let obj = {name: 'xn213', location: {city: '北京'}}
/** * Array 類型 */
// let obj = [1, 2, 3]
let xn = new Proxy(obj, {
get(target, key){
console.log('獲取值:', key)
// return target[key]
return Reflect.get(target, key, )
},
set(target, key, val){
console.log('修改值:', key, val)
// target[key] = val
// return true // 這裏return true 能夠簡單解決數組報錯的問題(理解)
return Reflect.set(target, key, val)
}
})
// 對象
// xn.name = 'xn2113'
// console.log(xn.name) // xn2113
// 1. 對象嵌套 不觸發
// 修改 location 的 city 不會觸發 city set(){}
// // 獲取值: location
// // 獲取值: location
// // 杭州
xn.location.city = '杭州'
console.log(xn.location.city) // 杭州
// 2. 數組屢次觸發
// xn.push(4)
/** push 從後面添加, 會觸發兩次 * 獲取值 push * 獲取值 length * 修改值 3 4 * 修改值 length 4 */
// xn.unshift(4)
/** unshift 從前面添加, 就會觸發不止兩次 * 獲取值 unshift * 獲取值 length * 獲取值 2 * 修改值 3 3 * 獲取值 1 * 修改值 2 2 * 獲取值 0 * 修改值 1 1 * 修改值 0 4 * 修改值 length 4 */
複製代碼
// 定義 reactive
function reactive(target){
// 查詢緩存
...
// 響應式
observed = new Proxy(target, baseHandler)
// 設置緩存
...
// 返回(觀測到的)響應後的對象
return observed
}
複製代碼
// 設置兩個緩存 舊查新 新查舊
let toProxy = new WeakMap() // 根據原始數據查詢響應後的數據
let toRaw = new WeakMap() // 根據響應後的數據查詢原始數據
複製代碼
// 定義 handler對象 > baseHandler
const baseHandler = {
get(target, key){
// 收集依賴 後返回數據
track(target, key)
},
set(target, key, val){
// 觸發更新 後返回數據
trigger(target, key, info)
}
}
複製代碼
↓
// 收集依賴
function track(target, key){...}
複製代碼
↓
// 觸發更新
function trigger(target, key, info){...}
複製代碼
↓
function effect(fn, options={}){...}
複製代碼
↓
function createReactiveEffect(fn, options){...}
複製代碼
↓
function computed(fn){...}
複製代碼
<div id="app"></div>
<button id="btn">點我, 又長大了...</button>
<script src="./xn-vue3.js"></script>
<script> const root = document.querySelector('#app') const btn = document.querySelector('#btn') const obj = reactive({ name: '本猿', age: '213' }) let double = computed(() => obj.age * 2) effect(() => { root.innerHTML = ` <h1>${obj.name}今年${obj.age}歲了, 乘以2是${double.value}</h1> ` }) btn.addEventListener('click', () => { obj.age++ }, false) </script>
複製代碼
// 設置兩個緩存
let toProxy = new WeakMap() // 根據原始數據查詢響應後的數據
let toRaw = new WeakMap() // 根據響應後的數據查詢原始數據
const isObject = val => val !== null && typeof val === 'object'
// handler 對象,其屬性是當執行一個操做時定義代理的行爲的函數
const baseHandler = {
get(target, key){
const res = Reflect.get(target, key)
// 收集依賴
track(target, key)
// 遞歸查找
// 這裏暫做簡單判斷, 能夠嚴謹用isObject 工具
// return typeof res == 'object' ? reactive(res) : res
return isObject(res) ? reactive(res) : res
},
set(target, key, val){
const info = {oldValue: target[key], newValue: val}
const res = Reflect.set(target, key, val)
// 觸發更新
trigger(target, key, info)
return res
}
}
function reactive(target){
// 查詢緩存
let observed = toProxy.get(target)
if(observed){
return observed
}
if(toRaw.get(target)){
return target
}
// 響應式
observed = new Proxy(target, baseHandler)
// 設置緩存
toProxy.set(target, observed)
toRaw.set(observed, target)
return observed
}
let effectStack = []
let targetMap = new WeakMap() // 存儲 effect
// {
// target: {
// age: [] (set),
// name: [effect]
// }
// }
function trigger(target, key, info){
// 觸發更新
const depsMap = targetMap.get(target)
if(depsMap === undefined){
return
}
const effects = new Set()
const computedRunners = new Set()
if(key){
let deps = depsMap.get(key)
deps.forEach(effect => {
if(effect.computed){
computedRunners.add(effect)
}else{
effects.add(effect)
}
})
}
// const run = effect => effect()
effects.forEach(effect => effect())
computedRunners.forEach(effect => effect())
}
function track(target, key){
let effect = effectStack[effectStack.length - 1]
if(effect){
let depsMap = targetMap.get(target)
if(depsMap === undefined){
depsMap = new Map()
targetMap.set(target, depsMap)
}
let dep = depsMap.get(key)
if(dep === undefined){
dep = new Set()
depsMap.set(key, dep)
}
if(!dep.has(effect)){
dep.add(effect)
effect.deps.push(dep)
}
}
}
function effect(fn, options={}){
let e = createReactiveEffect(fn, options)
// 沒有考慮 computed
e()
return e
}
// 高階函數
function createReactiveEffect(fn, options){
const effect = function effect(...args){
return run(effect, fn, args)
}
effect.deps = []
effect.computed = options.computed
effect.lazy = options.lazy
return effect
}
function run(effect, fn, args){
if(effectStack.indexOf(effect) === -1){
try {
effectStack.push(effect)
return fn(...args)
}
finally {
effectStack.pop() // 清空
}
}
}
function computed(fn){
// 首次不運行computed
const runner = effect(fn, {computed: true, lazy: true})
return {
effect: runner,
get value(){
return runner()
}
}
}
複製代碼
路漫漫兮...數組