vue

router

  • new Vue配置router , 會給vue實例對象創(chuàng)建兩個對象屬性,$router $route $router路由對象可以調(diào)用路由相關(guān)方法,$route存儲當(dāng)前路由相關(guān)數(shù)據(jù)

  • 使用步驟
    1 Vue.use(router)注冊路由插件
    2 創(chuàng)建路由配置
    3 vue實例中注冊router對象

  • 動態(tài)路由可以在路由配置中配置props:true 在組件中通過prop屬性聲明訪問對應(yīng)數(shù)據(jù)

  • hash模式 基于錨點 錨點 變化觸發(fā)onhashchange事件 改變的#后面的值

  • history模式 基于h5中的history.pushState() 改變地址欄地址 并把地址記錄到訪問歷史中(不會向服務(wù)器發(fā)送請求)history.popstate()(前進后退按鈕觸發(fā),獲取通過js前進后退history.go(n) / history.forward() )監(jiān)聽瀏覽器歷史的變化 history.replaceState() ie10后才支持 部署服務(wù)器刷新瀏覽器會出現(xiàn)404,原因是會去服務(wù)器請求對應(yīng)資源找不到,需要服務(wù)器配置對應(yīng)地址,找不到重定向到index.html

  • node服務(wù)器配置history

  • 刷新頁面瀏覽器向服務(wù)器發(fā)送對應(yīng)地址請求,服務(wù)器接受請求找不到對應(yīng)資源,返回index.html ,瀏覽器根據(jù)請求地址(如/about)加載再去加載對應(yīng)路由進行顯示

const path = require('path')
// 導(dǎo)入處理 history 模式的模塊
const history = require('connect-history-api-fallback')
// 導(dǎo)入 express
const express = require('express')

const app = express()
// 注冊處理 history 模式的中間件
app.use(history())
// 處理靜態(tài)資源的中間件,網(wǎng)站根目錄 ../web
app.use(express.static(path.join(__dirname, '../web')))

// 開啟服務(wù)器,端口是 3000
app.listen(3000, () => {
  console.log('服務(wù)器開啟,端口:3000')
})

nginx配置history

  • nginx目錄運行 start nginx
  • 重啟 nginx.exe -s reload
    -nginx config配置
   location   / {
          root   html;
          index index.html index.htm;  
          try_files $uri $uri/  /index.html;   //嘗試匹配當(dāng)前路徑資源 找不到返回index.html  返回html客戶端根據(jù)index去匹配對應(yīng)路由地址進行顯示
        }

自定義vueRouter

  • Vue.use接受函數(shù)或者對象 ,傳入函數(shù)會調(diào)用該函數(shù),傳入對象,會調(diào)用對象的install方法
  • VueRouter 是一個類 有個靜態(tài)方法install
let _Vue = ''
export default class VueRouter {
    static install(Vue) { //第一個參數(shù)為vue的構(gòu)造函數(shù)
        //1 判斷當(dāng)前插件是否被安裝
        if (VueRouter.install.installed) {
            return
        }
        VueRouter.install.installed = true
        //2 把Vue的構(gòu)造函數(shù)記錄在全局
        _Vue = Vue
        //3 把創(chuàng)建Vue的實例傳入的router對象注入到Vue實例
        // _Vue.prototype.$router = this.$options.router
        _Vue.mixin({
            beforeCreate() {
                if (this.$options.router) {
                    _Vue.prototype.$router = this.$options.router
                }
            }
        })
    }

    constructor(options) {
        this.options = options // 路由配置
        this.routeMap = {} // key對應(yīng)路由路勁 value對應(yīng)組件
        this.data = _Vue.observable({
            current: '/'
        })
        this.init()
    }

    init() {
        this.createRouteMap(this.options)
        this.initEvent()
        this.createComponent()
    }

    initEvent() {
        //監(jiān)聽前進后退
        window.addEventListener('popstate', () => {
            this.data.current = window.location.pathname
        })
    }

    createRouteMap(options) {
        options.routes.forEach(item => {
            this.routeMap[item.path] = item.component
        })
    }

    createComponent() {
        const self = this
        _Vue.component('router-link', {
            render(h) {
                return h('a', {
                    attrs: {
                        href: this.to
                    },
                    on: {
                        click: this.handleClick
                    }
                }, [this.$slots.default])
            },
            props: {
                to: String
            },
            methods: {
                handleClick(e) {
                    // 改變地址欄參數(shù)
                    history.pushState({}, '', this.to)
                    // 切換對應(yīng)組件
                    this.$router.data.current = this.to
                    e.preventDefault()
                }
            }
        })
        _Vue.component('router-view', {
            render(h) {
                const currentComponent = self.routeMap[self.data.current]
                if (!currentComponent) {
                    return h(self.routeMap['*'])
                } else {
                    return h(currentComponent)
                }
            }
        })
    }
}


vue響應(yīng)式原理
1.vue2.0 Object.defineProperty給對象綁定get set數(shù)據(jù)劫持,讀取對象屬性,觸發(fā)get,設(shè)置對象屬性觸發(fā)set

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>defineProperty 多個成員</title>
</head>
<body>
  <div id="app">
    hello
  </div>
  <script>
    // 模擬 Vue 中的 data 選項
    let data = {
      msg: 'hello',
      count: 10
    }

    // 模擬 Vue 的實例
    let vm = {}

    proxyData(data)

    function proxyData(data) {
      // 遍歷 data 對象的所有屬性
      Object.keys(data).forEach(key => {
        // 把 data 中的屬性,轉(zhuǎn)換成 vm 的 setter/setter
        Object.defineProperty(vm, key, {
          enumerable: true,
          configurable: true,
          get () {
            console.log('get: ', key, data[key])
            return data[key]
          },
          set (newValue) {
            console.log('set: ', key, newValue)
            if (newValue === data[key]) {
              return
            }
            data[key] = newValue
            // 數(shù)據(jù)更改,更新 DOM 的值
            document.querySelector('#app').textContent = data[key]
          }
        })
      })
    }

    // 測試
    vm.msg = 'Hello World'
    console.log(vm.msg)
  </script>
</body>
</html>
  1. vue3.0 Proxy ie不支持
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Proxy</title>
</head>
<body>
  <div id="app">
    hello
  </div>
  <script>
    // 模擬 Vue 中的 data 選項
    let data = {
      msg: 'hello',
      count: 0
    }

    // 模擬 Vue 實例
    let vm = new Proxy(data, {
      // 執(zhí)行代理行為的函數(shù)
      // 當(dāng)訪問 vm 的成員會執(zhí)行
      get (target, key) {
        console.log('get, key: ', key, target[key])
        return target[key]
      },
      // 當(dāng)設(shè)置 vm 的成員會執(zhí)行
      set (target, key, newValue) {
        console.log('set, key: ', key, newValue)
        if (target[key] === newValue) {
          return
        }
        target[key] = newValue
        document.querySelector('#app').textContent = target[key]
      }
    })

    // 測試
    vm.msg = 'Hello World'
    console.log(vm.msg)
  </script>
</body>
</html>

發(fā)布訂閱模式

  • $on 注冊事件 $emit發(fā)布事件
<!DOCTYPE html>
<html lang="cn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>發(fā)布訂閱模式</title>
</head>
<body>
  <script>
    // 事件觸發(fā)器
    class EventEmitter {
      constructor () {
        // { 'click': [fn1, fn2], 'change': [fn] }
        this.subs = Object.create(null)
      }

      // 注冊事件
      $on (eventType, handler) {
        this.subs[eventType] = this.subs[eventType] || []
        this.subs[eventType].push(handler)
      }

      // 觸發(fā)事件
      $emit (eventType) {
        if (this.subs[eventType]) {
          this.subs[eventType].forEach(handler => {
            handler()
          })
        }
      }
    }

    // 測試
    let em = new EventEmitter()
    em.$on('click', () => {
      console.log('click1')
    })
    em.$on('click', () => {
      console.log('click2')
    })

    em.$emit('click')
  </script>
</body>
</html>

觀察者模式

  • 發(fā)布者Dep記錄所有訂閱者watcher所有更新操作,存儲在數(shù)組中,當(dāng)需要更新的時候調(diào)用notify遍歷數(shù)組,調(diào)用update方法更新
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>觀察者模式</title>
</head>
<body>
  <script>
    // 發(fā)布者-目標(biāo)
    class Dep {
      constructor () {
        // 記錄所有的訂閱者
        this.subs = []
      }
      // 添加訂閱者
      addSub (sub) {
        if (sub && sub.update) {
          this.subs.push(sub)
        }
      }
      // 發(fā)布通知
      notify () {
        this.subs.forEach(sub => {
          sub.update()
        })
      }
    }
    // 訂閱者-觀察者
    class Watcher {
      update () {
        console.log('update')
      }
    }

    // 測試
    let dep = new Dep()    //發(fā)布者
    let watcher = new Watcher()  //訂閱者

    dep.addSub(watcher)

    dep.notify()
  </script>
</body>
</html>

虛擬DOM

  • snabbdom
  • cnpm i snabbdom -D
  • snabbdom 核心庫不能處理屬性樣式,需要使用對應(yīng)模塊
// import { h } from 'snabbdom'
import { init } from 'snabbdom/build/package/init'
import { thunk } from 'snabbdom/build/package/thunk'
import { h } from 'snabbdom/build/package/h'
let patch = init([])   //返回值對比兩個vnode的差異更新到真實DOM
//第一個參數(shù)標(biāo)簽加選擇器
//第二個參數(shù)為標(biāo)簽中的內(nèi)容
let vnode = h('div#container.cls', 'hello world')
let app = document.getElementById('app')
//第一個參數(shù):可以是DOM,內(nèi)部會把DOM轉(zhuǎn)換為vnode
//第二個參數(shù)vnode
//返回值vnode
// let oldValue = patch(app, vnode)
vnode = h('div','new Value')
vnode = h('div',[
    h('h1','標(biāo)題'),
    h('h2','二級標(biāo)題')
])
let oldValue = patch(app, vnode)
setTimeout(function(){
    vnode = h('div',[
        h('h1','標(biāo)題111'),
        h('h2','二級標(biāo)題')
    ])
    patch(oldValue,vnode)
},2000)

模塊

  • attributes 設(shè)置DOM屬性 處理布爾類型
  • props 設(shè)置DOM屬性 不處理布爾類型
  • class 切換樣式
  • dataset 設(shè)置自定義屬性
  • eventlisters 注冊移除事件
  • style設(shè)置行內(nèi)樣式 支持動畫
使用
// import { h } from 'snabbdom'
import { init } from 'snabbdom/build/package/init'
import { h } from 'snabbdom/build/package/h'
import { styleModule } from "snabbdom/build/package/modules/style"
import { eventListenersModule } from "snabbdom/build/package/modules/eventlisteners"
let patch = init([styleModule, eventListenersModule])
let vnode = h('div', {
    style:
        { backgroundColor: '#aaa' },
    on:{
        click:function(){
             alert('click')
        }
    }
},[
    h('h1','hello'),
    h('h2','二級標(biāo)題')
])
let app = document.getElementById('app')
patch(app,vnode)

snabbdom 源碼

核心

  • 使用h函數(shù)創(chuàng)建js對象(vnode)描述真實DOM
  • init()設(shè)置模塊,返回patch函數(shù)
  • patch函數(shù)比較新舊兩個Vnode
  • 把變化的內(nèi)容弄更新到真實DOM上

vscode 調(diào)試

ctrl+鼠標(biāo)左鍵 跳轉(zhuǎn)函數(shù)定義 alt+鍵盤方向左回退

h函數(shù) - 創(chuàng)建vnode
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容