整合營銷服務商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          前端架構師基于京東門戶編寫的7篇html、css實戰(zhàn),代碼免費分享!

          情提示:以下代碼是我們邀請的前端架構師基于京東門戶編寫的html、css代碼,現(xiàn)在免費分享給大家,獲取的方式為:

          關注此頭條號“互聯(lián)網(wǎng)IT信息”——>私信發(fā)送 “京東html” ,即可得到源代碼的獲取方式。

          幾小編分別利用Python正則表達式和BeautifulSoup爬取了京東網(wǎng)商品信息,今天小編利用Xpath來為大家演示一下如何實現(xiàn)京東商品信息的精準匹配~~

          HTML文件其實就是由一組尖括號構成的標簽組織起來的,每一對尖括號形式一個標簽,標簽之間存在上下關系,形成標簽樹;XPath 使用路徑表達式在 XML 文檔中選取節(jié)點。節(jié)點是通過沿著路徑或者 step 來選取的。

          京東網(wǎng)狗糧商品

          首先進入京東網(wǎng),輸入自己想要查詢的商品,向服務器發(fā)送網(wǎng)頁請求。在這里小編仍以關鍵詞“狗糧”作為搜索對象,之后得到后面這一串網(wǎng)址: https://search.jd.com/Search?keyword=%E7%8B%97%E7%B2%AE&enc=utf-8,其中參數(shù)的意思就是我們輸入的keyword,在本例中該參數(shù)代表“狗糧”,具體詳情可以參考Python大神用正則表達式教你搞定京東商品信息。所以,只要輸入keyword這個參數(shù)之后,將其進行編碼,就可以獲取到目標URL。之后請求網(wǎng)頁,得到響應,爾后利用bs4選擇器進行下一步的數(shù)據(jù)采集。

          商品信息在京東官網(wǎng)上的部分網(wǎng)頁源碼如下圖所示:

          狗糧信息在京東官網(wǎng)上的網(wǎng)頁源碼

          仔細觀察源碼,可以發(fā)現(xiàn)我們所需的目標信息是存在<li data-sku="/*/*/*/*/*" class="gl-item">標簽下的,那么接下來我們就像剝洋蔥一樣,一層一層的去獲取我們想要的信息。

          通常URL編碼的方式是把需要編碼的字符轉化為%xx的形式,一般來說URL的編碼是基于UTF-8的,當然也有的于瀏覽器平臺有關。在Python的urllib庫中提供了quote方法,可以實現(xiàn)對URL的字符串進行編碼,從而可以進入到對應的網(wǎng)頁中去。

          在線復制Xpath表達式

          很多小伙伴都覺得Xpath表達式很難寫,其實掌握了基本的用法也就不難了。在線復制Xpath表達式如上圖所示,可以很方便的復制Xpath表達式。但是通過該方法得到的Xpath表達式放在程序中一般不能用,而且長的沒法看。所以Xpath表達式一般還是要自己親自上手。

          直接上代碼,利用Xpath去提取目標信息,如商品的名字、鏈接、圖片和價格,具體的代碼如下圖所示:

          爬蟲代碼

          在這里,小編告訴大家一個Xpath表達式匹配技巧。之前看過好幾篇文章,大佬們都推薦Xpath表達式使用嵌套匹配的方式。在本例中,首先定義items,如下所示:

          items = selector.xpath('//li[@class="gl-item"]')

          之后通過range函數(shù),逐個從網(wǎng)頁中進行匹配目標信息,而不是直接通過復制Xpath表達式的方式一步到位。希望小伙伴們以后都可以少入這個坑~~

          最后得到的效果圖如下所示:

          最終效果圖

          新鮮的狗糧再一次出爐咯~

          小伙伴們,有沒有發(fā)現(xiàn)利用Xpath來獲取目標信息比正則表達式要簡單一些呢?

          想學習更多Python網(wǎng)絡爬蟲與數(shù)據(jù)挖掘知識,可前往專業(yè)網(wǎng)站:http://pdcfighting.com/

          、Vue3結構分析

          1、Vue2與Vue3的對比

          ?對TypeScript支持不友好(所有屬性都放在了this對象上,難以推倒組件的數(shù)據(jù)類型)

          ?大量的API掛載在Vue對象的原型上,難以實現(xiàn)TreeShaking。

          ?架構層面對跨平臺dom渲染開發(fā)支持不友好,vue3允許自定義渲染器,擴展能力強。

          ?CompositionAPI。受ReactHook啟發(fā)

          ?對虛擬DOM進行了重寫、對模板的編譯進行了優(yōu)化操作...

          2、Vue3設計思想

          ?Vue3.0更注重模塊上的拆分,在2.0中無法單獨使用部分模塊。需要引入完整的Vuejs(例如只想使用使用響應式部分,但是需要引入完整的Vuejs), Vue3中的模塊之間耦合度低,模塊可以獨立使用。拆分模塊

          ?Vue2中很多方法掛載到了實例中導致沒有使用也會被打包(還有很多組件也是一樣)。通過構建工具Tree-shaking機制實現(xiàn)按需引入,減少用戶打包后體積。重寫API

          ?Vue3允許自定義渲染器,擴展能力強。不會發(fā)生以前的事情,改寫Vue源碼改造渲染方式。擴展更方便

          依然保留了Vue2的特點:

          依舊是聲明式框架,底層渲染邏輯不關心(命令式比較關注過程,可以控制怎么寫最優(yōu)?編寫過程不同),如for和reduce

          采用虛擬DOM

          區(qū)分編譯時和運行時

          內(nèi)部區(qū)分了編譯時(模板?編程成js代碼,一般在構建工具中使用)和運行時

          簡單來說,Vue3 框架更小,擴展更加方便

          3、monorepo管理項目

          Monorepo 是管理項目代碼的一個方式,指在一個項目倉庫(repo)中管理多個模塊/包(package)。也就是說是一種將多個package放在一個repo中的代碼管理模式。Vue3內(nèi)部實現(xiàn)了一個模塊的拆分, Vue3源碼采用 Monorepo 方式進行管理,將模塊拆分到package目錄中。

          ?一個倉庫可維護多個模塊,不用到處找倉庫

          ?方便版本管理和依賴管理,模塊之間的引用,調用都非常方便

          ?每個包可以獨立發(fā)布

          早期使用yarn workspace + lerna來管理項目,后面是pnpm

          pnpm介紹

          快速,節(jié)省磁盤空間的包管理器,主要采用符號鏈接的方式管理模塊

          1.快速

          2.高效利用磁盤空間

          pnpm 內(nèi)部使用基于內(nèi)容尋址的文件系統(tǒng)來存儲磁盤上所有的文件,這個文件系統(tǒng)出色的地方在于:

          ?不會重復安裝同一個包。用 npm/yarn 的時候,如果 100 個項目都依賴 lodash,那么 lodash 很可能就被安裝了 100 次,磁盤中就有 100 個地方寫入了這部分代碼。但在使用 pnpm 只會安裝一次,磁盤中只有一個地方寫入,后面再次使用都會直接使用hardlink(硬鏈接)

          ?即使一個包的不同版本,pnpm 也會極大程度地復用之前版本的代碼。比如 lodash 有 100 個文件,更新版本之后多了一個文件,那么磁盤當中并不會重新寫入 101 個文件,而是保留原來的 100 個文件的hardlink,僅僅寫入那一個新增的文件。

          1.支持Monorepo

          pnpm 與 npm/yarn 一個很大的不同就是支持了 monorepo

          1.安全性高

          之前在使用 npm/yarn 的時候,由于 node_module 的扁平結構,如果 A 依賴 B, B 依賴 C,那么 A 當中是可以直接使用 C 的,但問題是 A 當中并沒有聲明 C 這個依賴。因此會出現(xiàn)這種非法訪問的情況。但 pnpm自創(chuàng)了一套依賴管理方式,很好地解決了這個問題,保證了安全性

          默認情況下,pnpm 則是通過使用符號鏈接的方式僅將項目的直接依賴項添加到node_modules的根目錄下。

          ??安裝和初始化

          ?全局安裝(node版本>16)

          npm install pnpm -g
          
          

          ?初始化

          pnpm init
          
          

          配置workspace

          根目錄創(chuàng)建pnpm-workspace.yaml

          packages:
            - 'packages/*'
          
          

          將packages下所有的目錄都作為包進行管理。這樣我們的Monorepo就搭建好了。確實比lerna + yarn workspace更快捷

          4、項目結構

          packages

          ?reactivity:響應式系統(tǒng)

          ?runtime-core:與平臺無關的運行時核心 (可以創(chuàng)建針對特定平臺的運行時 - 自定義渲染器)

          ?runtime-dom: 針對瀏覽器的運行時。包括DOM API,屬性,事件處理等

          ?runtime-test:用于測試

          ?server-renderer:用于服務器端渲染

          ?compiler-core:與平臺無關的編譯器核心

          ?compiler-dom: 針對瀏覽器的編譯模塊

          ?compiler-ssr: 針對服務端渲染的編譯模塊

          ?template-explorer:用于調試編譯器輸出的開發(fā)工具

          ?shared:多個包之間共享的內(nèi)容

          ?vue:完整版本,包括運行時和編譯器

                                              +---------------------+
                                              |                     |
                                              |  @vue/compiler-sfc  |
                                              |                     |
                                              +-----+--------+------+
                                                    |        |
                                                    v        v
                                +---------------------+    +----------------------+
                                |                     |    |                      |
                  +------------>|  @vue/compiler-dom  +--->|  @vue/compiler-core  |
                  |             |                     |    |                      |
             +----+----+        +---------------------+    +----------------------+
             |         |
             |   vue   |
             |         |
             +----+----+        +---------------------+    +----------------------+    +-------------------+
                  |             |                     |    |                      |    |                   |
                  +------------>|  @vue/runtime-dom   +--->|  @vue/runtime-core   +--->|  @vue/reactivity  |
                                |                     |    |                      |    |                   |
                                +---------------------+    +----------------------+    +-------------------+
          
          

          scripts

          Vue3在開發(fā)環(huán)境使用esbuild打包,生產(chǎn)環(huán)境采用rollup打包

          包的相互依賴

          安裝

          把packages/shared安裝到packages/reactivity

          pnpm install @vue/shared@workspace --filter @vue/reactivity
          
          

          ??使用

          在reactivity/src/computed.ts中引入shared中相關方法

          import { isFunction, NOOP } from '@vue/shared' // ts引入會報錯
          
          const onlyGetter = isFunction(getterOrOptions)
            if (onlyGetter) {
              ...
            } else {
              ...
            }
          ...
          
          

          tips:@vue/shared引入會報錯,需要在tsconfig.json中配置

          {
            "compilerOptions": {
              "baseUrl": ".",
              "paths": {
                "@vue/compat": ["packages/vue-compat/src"],
                "@vue/*": ["packages/*/src"],
                "vue": ["packages/vue/src"]
              }
            },
          }
          
          

          5、打包

          所有包的入口均為src/index.ts這樣可以實現(xiàn)統(tǒng)一打包.

          ?reactivity/package.json

          {
            "name": "@vue/reactivity",
            "version": "3.2.45",
            "main": "index.js",
            "module":"dist/reactivity.esm-bundler.js",
            "unpkg": "dist/reactivity.global.js",
            "buildOptions": {
              "name": "VueReactivity",
              "formats": [
                "esm-bundler",
                "cjs",
                "global"
              ]
            }
          }
          
          

          ?shared/package.json

          {
              "name": "@vue/shared",
              "version": "3.2.45",
              "main": "index.js",
              "module": "dist/shared.esm-bundler.js",
              "buildOptions": {
                  "formats": [
                      "esm-bundler",
                      "cjs"
                  ]
              }
          }
          
          

          formats為自定義的打包格式,有esm-bundler在構建工具中使用的格式、esm-browser在瀏覽器中使用的格式、cjs在node中使用的格式、global立即執(zhí)行函數(shù)的格式

          開發(fā)環(huán)境esbuild打包

          ??開發(fā)時 執(zhí)行腳本, 參數(shù)為要打包的模塊

          "scripts": {
              "dev": "node scripts/dev.js reactivity -f global"
          }
          
          
          // Using esbuild for faster dev builds.
          // We are still using Rollup for production builds because it generates
          // smaller files w/ better tree-shaking.
          
          // @ts-check
          const { build } = require('esbuild')
          const nodePolyfills = require('@esbuild-plugins/node-modules-polyfill')
          const { resolve, relative } = require('path')
          const args = require('minimist')(process.argv.slice(2))
          
          const target = args._[0] || 'vue'
          const format = args.f || 'global'
          const inlineDeps = args.i || args.inline
          const pkg = require(resolve(__dirname, `../packages/${target}/package.json`))
          
          // resolve output
          const outputFormat = format.startsWith('global')
            ? 'iife'
            : format === 'cjs'
            ? 'cjs'
            : 'esm'
          
          const postfix = format.endsWith('-runtime')
            ? `runtime.${format.replace(/-runtime$/, '')}`
            : format
          
          const outfile = resolve(
            __dirname,
            `../packages/${target}/dist/${
              target === 'vue-compat' ? `vue` : target
            }.${postfix}.js`
          )
          const relativeOutfile = relative(process.cwd(), outfile)
          
          // resolve externals
          // TODO this logic is largely duplicated from rollup.config.js
          let external = []
          if (!inlineDeps) {
            // cjs & esm-bundler: external all deps
            if (format === 'cjs' || format.includes('esm-bundler')) {
              external = [
                ...external,
                ...Object.keys(pkg.dependencies || {}),
                ...Object.keys(pkg.peerDependencies || {}),
                // for @vue/compiler-sfc / server-renderer
                'path',
                'url',
                'stream'
              ]
            }
          
            if (target === 'compiler-sfc') {
              const consolidateDeps = require.resolve('@vue/consolidate/package.json', {
                paths: [resolve(__dirname, `../packages/${target}/`)]
              })
              external = [
                ...external,
                ...Object.keys(require(consolidateDeps).devDependencies),
                'fs',
                'vm',
                'crypto',
                'react-dom/server',
                'teacup/lib/express',
                'arc-templates/dist/es5',
                'then-pug',
                'then-jade'
              ]
            }
          }
          
          build({
            entryPoints: [resolve(__dirname, `../packages/${target}/src/index.ts`)],
            outfile,
            bundle: true,
            external,
            sourcemap: true,
            format: outputFormat,
            globalName: pkg.buildOptions?.name,
            platform: format === 'cjs' ? 'node' : 'browser',
            plugins:
              format === 'cjs' || pkg.buildOptions?.enableNonBrowserBranches
                ? [nodePolyfills.default()]
                : undefined,
            define: {
              __COMMIT__: `"dev"`,
              __VERSION__: `"${pkg.version}"`,
              __DEV__: `true`,
              __TEST__: `false`,
              __BROWSER__: String(
                format !== 'cjs' && !pkg.buildOptions?.enableNonBrowserBranches
              ),
              __GLOBAL__: String(format === 'global'),
              __ESM_BUNDLER__: String(format.includes('esm-bundler')),
              __ESM_BROWSER__: String(format.includes('esm-browser')),
              __NODE_JS__: String(format === 'cjs'),
              __SSR__: String(format === 'cjs' || format.includes('esm-bundler')),
              __COMPAT__: String(target === 'vue-compat'),
              __FEATURE_SUSPENSE__: `true`,
              __FEATURE_OPTIONS_API__: `true`,
              __FEATURE_PROD_DEVTOOLS__: `false`
            },
            watch: {
              onRebuild(error) {
                if (!error) console.log(`rebuilt: ${relativeOutfile}`)
              }
            }
          }).then(() => {
            console.log(`watching: ${relativeOutfile}`)
          })
          
          
          

          生產(chǎn)環(huán)境rollup打包

          具體代碼參考rollup.config.mjs

          build.js

          ?二、Vue3中Reactivity模塊

          1、vue3對比vue2的響應式變化

          ?在Vue2的時候使用defineProperty來進行數(shù)據(jù)的劫持, 需要對屬性進行重寫添加getter及setter性能差。

          ?當新增屬性和刪除屬性時無法監(jiān)控變化。需要通過$set、$delete實現(xiàn)

          ?數(shù)組不采用defineProperty來進行劫持 (浪費性能,對所有索引進行劫持會造成性能浪費)需要對數(shù)組單獨進行處理

          Vue3中使用Proxy來實現(xiàn)響應式數(shù)據(jù)變化。從而解決了上述問題

          2、CompositionAPI

          ?在Vue2中采用的是OptionsAPI, 用戶提供的data,props,methods,computed,watch等屬性 (用戶編寫復雜業(yè)務邏輯會出現(xiàn)反復橫跳問題)

          ?Vue2中所有的屬性都是通過this訪問,this存在指向明確問題

          ?Vue2中很多未使用方法或屬性依舊會被打包,并且所有全局API都在Vue對象上公開。Composition API對 tree-shaking 更加友好,代碼也更容易壓縮。

          ?組件邏輯共享問題, Vue2 采用mixins 實現(xiàn)組件之間的邏輯共享; 但是會有數(shù)據(jù)來源不明確,命名沖突等問題。 Vue3采用CompositionAPI 提取公共邏輯非常方便

          簡單的組件仍然可以采用OptionsAPI進行編寫,compositionAPI在復雜的邏輯中有著明顯的優(yōu)勢~。reactivity模塊中就包含了很多我們經(jīng)常使用到的API例如:computed、reactive、ref、effect等

          3、基本使用

          const { effect, reactive } = VueReactivity
          // console.log(effect, reactive);
          const state = reactive({name: 'qpp', age:18, address: {city: '南京'}})
          console.log(state.address);
          effect(()=>{
              console.log(state.name)
          })
          
          

          4、reactive實現(xiàn)

          import { mutableHandlers } from'./baseHandlers'; 
          // 代理相關邏輯import{ isObject }from'./util';// 工具方法
          export function reactive(target: object) {
            // if trying to observe a readonly proxy, return the readonly version.
            if (isReadonly(target)) {
              return target
            }
            return createReactiveObject(
              target,
              false,
              mutableHandlers,
              mutableCollectionHandlers,
              reactiveMap
            )
          }
          function createReactiveObject(target, baseHandler){
              if(!isObject(target)){
                  return target;
              }
              ...
              const observed =new Proxy(target, baseHandler);
              return observed
          }
          
          

          baseHandlers

          import { isObject, hasOwn, hasChanged } from"@vue/shared";
          import { reactive } from"./reactive";
          const get = createGetter();
          const set = createSetter();
          function createGetter(){
              return function get(target, key, receiver){
                  // 對獲取的值進行放射
                  const res = Reflect.get(target, key, receiver);
                  console.log('屬性獲取',key)
                  if(isObject(res)){// 如果獲取的值是對象類型,則返回當前對象的代理對象
                      return reactive(res);
                  }
                  return res;
              }
          }
          function createSetter(){
              return function set(target, key, value, receiver){
                  const oldValue = target[key];
                  const hadKey =hasOwn(target, key);
                  const result = Reflect.set(target, key, value, receiver);
                  if(!hadKey){
                      console.log('屬性新增',key,value)
                  }else if(hasChanged(value, oldValue)){
                      console.log('屬性值被修改',key,value)
                  }
                  return result;
              }
          }
          export const mutableHandlers ={
              get,// 當獲取屬性時調用此方法
              set// 當修改屬性時調用此方法
          }
          
          

          這里我只選了對最常用到的get和set方法的代碼,還應該有has、deleteProperty、ownKeys。這里為了快速掌握核心流程就先暫且跳過這些代碼

          5、effect實現(xiàn)

          我們再來看effect的代碼,默認effect會立即執(zhí)行,當依賴的值發(fā)生變化時effect會重新執(zhí)行

          export let activeEffect = undefined;
          // 依賴收集的原理是 借助js是單線程的特點, 默認調用effect的時候會去調用proxy的get,此時讓屬性記住
          // 依賴的effect,同理也讓effect記住對應的屬性
          // 靠的是數(shù)據(jù)結構 weakMap : {map:{key:new Set()}}
          // 稍后數(shù)據(jù)變化的時候 找到對應的map 通過屬性出發(fā)set中effect
          function cleanEffect(effect) {
              // 需要清理effect中存入屬性中的set中的effect 
              // 每次執(zhí)行前都需要將effect只對應屬性的set集合都清理掉
              // 屬性中的set 依然存放effect
              let deps = effect.deps
              for (let i = 0; i < deps.length; i++) {
                  deps[i].delete(effect)
              }
              effect.deps.length = 0;
          
          }
          
          // 創(chuàng)建effect時可以傳遞參數(shù),computed也是基于effect來實現(xiàn)的,只是增加了一些參數(shù)條件而已
          export function effect<T = any>(
              fn: () => T,
              options?: ReactiveEffectOptions    
          ){
              // 將用戶傳遞的函數(shù)編程響應式的effect
              const _effect = new ReactiveEffect(fn,options.scheduler);
              // 更改runner中的this
              _effect.run()
              const runner = _effect.run.bind(_effect);
              runner.effect = _effect; // 暴露effect的實例
              return runner// 用戶可以手動調用runner重新執(zhí)行
          }
          export class ReactiveEffect {
              public active = true;
              public parent = null;
              public deps = []; // effect中用了哪些屬性,后續(xù)清理的時候要使用
              constructor(public fn,public scheduler?) { } // 你傳遞的fn我會幫你放到this上
              // effectScope 可以來實現(xiàn)讓所有的effect停止
              run() {
                  // 依賴收集  讓熟悉和effect 產(chǎn)生關聯(lián)
                  if (!this.active) {
                      return this.fn();
                  } else {
                      try {
                          this.parent = activeEffect
                          activeEffect = this;
                          cleanEffect(this); // vue2 和 vue3中都是要清理的 
                          return this.fn(); // 去proxy對象上取值, 取之的時候 我要讓這個熟悉 和當前的effect函數(shù)關聯(lián)起來,稍后數(shù)據(jù)變化了 ,可以重新執(zhí)行effect函數(shù)
                      } finally {
                          // 取消當前正在運行的effect
                          activeEffect = this.parent;
                          this.parent = null;
                      }
                  }
              }
              stop() {
                  if (this.active) {
                      this.active = false;
                      cleanEffect(this);
                  }
              }
          }
          
          

          在effect方法調用時會對屬性進行取值,此時可以進行依賴收集。

          effect(()=>{
              console.log(state.name)
              // 執(zhí)行用戶傳入的fn函數(shù),會取到state.name,state.age... 會觸發(fā)reactive中的getter
              app.innerHTML = 'name:' + state.name + 'age:' + state.age + 'address' + state.address.city
              
          })
          
          

          6、依賴收集

          核心代碼

          // 收集屬性對應的effect
          export function track(target, type, key){}// 觸發(fā)屬性對應effect執(zhí)行
          export function trigger(target, type, key){}
          
          


          function createGetter(){
              return function get(target, key, receiver){
                  const res = Reflect.get(target, key, receiver);
                  // 取值時依賴收集
                  track(target, TrackOpTypes.GET, key);
                  if(isObject(res)){
                      return reactive(res);
                  }
                  return res;
              }
          }
          
          
          function createSetter(){
              return function set(target, key, value, receiver){
                  const oldValue = target[key];
                  const hadKey =hasOwn(target, key);
                  const result = Reflect.set(target, key, value, receiver);
                  if(!hadKey){
                      // 設置值時觸發(fā)更新 - ADD
                      trigger(target, TriggerOpTypes.ADD, key);
                  }else if(hasChanged(value, oldValue)){
                       // 設置值時觸發(fā)更新 - SET
                      trigger(target, TriggerOpTypes.SET, key, value, oldValue);
                  }
                  return result;
              }
          }
          
          

          track的實現(xiàn)

          const targetMap = new WeakMap();
          export function track(target: object, type: TrackOpTypes, key: unknown){
              if (shouldTrack && activeEffect) { // 上下文 shouldTrack = true
                  let depsMap = targetMap.get(target);
                  if(!depsMap){// 如果沒有map,增加map
                      targetMap.set(target,(depsMap =newMap()));
                  }
                  let dep = depsMap.get(key);// 取對應屬性的依賴表
                  if(!dep){// 如果沒有則構建set
                      depsMap.set(key,(dep =newSet()));
                  }
              
                  trackEffects(dep, eventInfo)
              }
          }
          
          export function trackEffects(
            dep: Dep,
            debuggerEventExtraInfo?: DebuggerEventExtraInfo
          ) {
            //let shouldTrack = false
            //if (effectTrackDepth <= maxMarkerBits) {
             // if (!newTracked(dep)) {
               // dep.n |= trackOpBit // set newly tracked
               // shouldTrack = !wasTracked(dep)
              //}
            //} else {
              // Full cleanup mode.
            //  shouldTrack = !dep.has(activeEffect!)
            } 
          
            if (!dep.has(activeEffect!) {
              dep.add(activeEffect!)
              activeEffect!.deps.push(dep)
              //if (__DEV__ && activeEffect!.onTrack) {
              //  activeEffect!.onTrack({
              //    effect: activeEffect!,
              //    ...debuggerEventExtraInfo!
              //  })
             // }
            }
          }
          
          

          trigger實現(xiàn)

          export function trigger(target, type, key){
              const depsMap = targetMap.get(target);
              if(!depsMap){
                  return;
              }
              const run=(effects)=>{
                  if(effects){ effects.forEach(effect=>effect()); }
              }
              // 有key 就找到對應的key的依賴執(zhí)行
              if(key !==void0){
                  run(depsMap.get(key));
              }
              // 數(shù)組新增屬性
              if(type == TriggerOpTypes.ADD){
                  run(depsMap.get(isArray(target)?'length':'');
              }}
          
          

          依賴關系



          作者:京東物流 喬盼盼

          來源:京東云開發(fā)者社區(qū) 自猿其說Tech 轉載請注明來源


          主站蜘蛛池模板: 无码人妻一区二区三区免费手机 | 在线观看亚洲一区二区| 国产日韩AV免费无码一区二区| 亚洲图片一区二区| 中文字幕一区二区三区视频在线| 成人区人妻精品一区二区三区 | 麻豆AV一区二区三区| 亚洲一区二区三区夜色| 国产日韩AV免费无码一区二区 | 日韩人妻无码一区二区三区综合部| 国产一区二区三区无码免费| 精品黑人一区二区三区| 久久精品午夜一区二区福利| 日韩人妻无码一区二区三区99 | 中文字幕精品无码一区二区三区| 国产91一区二区在线播放不卡| 无码人妻av一区二区三区蜜臀| 韩国女主播一区二区| 日本精品一区二区三本中文| 一区视频在线播放| 国产一区二区在线视频| 一区二区在线视频| 天天视频一区二区三区| 国产精品亚洲高清一区二区 | 无码国产精品一区二区免费vr| 亚洲视频一区二区在线观看| 日本在线视频一区二区| 国产在线第一区二区三区| 久久久久国产一区二区三区| 亚洲Av高清一区二区三区| 国产精品熟女视频一区二区| 日韩一区二区电影| 日本精品少妇一区二区三区 | 国产精品无码一区二区三区免费 | 一本久久精品一区二区| 相泽亚洲一区中文字幕| 精品无码国产一区二区三区麻豆| 国产成人无码aa精品一区| 亚洲一区二区三区播放在线| 无码精品久久一区二区三区 | 精品视频在线观看一区二区|