精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

新知識Get,Vue3是如何實現(xiàn)在Style中使用響應(yīng)式變量?

開發(fā) 前端
由于我們需要在回調(diào)中操作DOM,所以才需要使用flush: 'post'?,讓回調(diào)函數(shù)在組件渲染之后去執(zhí)行。由于在回調(diào)函數(shù)中會去讀取v-bind?綁定的響應(yīng)式變量,所以每次綁定的響應(yīng)式變量值變化后都會再次執(zhí)行調(diào)用watchPostEffect傳入的回調(diào)函數(shù),以此讓響應(yīng)式變量綁定的樣式保存更新。

前言

vue2的時候想必大家有遇到需要在style模塊中訪問script模塊中的響應(yīng)式變量,為此我們不得不使用css變量去實現(xiàn)。現(xiàn)在vue3已經(jīng)內(nèi)置了這個功能啦,可以在style中使用v-bind指令綁定script模塊中的響應(yīng)式變量,這篇文章我們來講講vue是如何實現(xiàn)在style中使用script模塊中的響應(yīng)式變量。注:本文中使用的vue版本為3.4.19。

看個demo

我們來看個簡單的demo,index.vue文件代碼如下:

<template>
  <div>
    <p>222</p>
    <span class="block">hello world</span>
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";

const primaryColor = ref("red");
</script>

<style scoped>
.block {
  color: v-bind(primaryColor);
}
</style>

我們在script模塊中定義了一個響應(yīng)式變量primaryColor,并且在style中使用v-bind指令將primaryColor變量綁定到color樣式上面。

我們在瀏覽器的network面板中來看看編譯后的js文件,如下圖:

圖片圖片

從上圖中可以看到在network面板中編譯后的index.vue文件有兩個,并且第二個里面有一些query參數(shù),其中的type=style就表示當前文件的內(nèi)容對應(yīng)的是style模塊。第一個index.vue對應(yīng)的是template和script模塊中的內(nèi)容。

我們來看看第一個index.vue,如下圖:

圖片圖片

從上圖中可以看到setup函數(shù)是script模塊編譯后的內(nèi)容,在setup函數(shù)中多了一個_useCssVars函數(shù),從名字你應(yīng)該猜到了,這個函數(shù)的作用是和css變量有關(guān)系。別著急,我們接下來會詳細去講_useCssVars函數(shù)。

我們再來看看第二個index.vue,如下圖:

圖片圖片

從上圖中可以看到這個index.vue確實對應(yīng)的是style模塊中的內(nèi)容,并且原本的color: v-bind(primaryColor);已經(jīng)變成了color: var(--c845efc6-primaryColor);。

很明顯瀏覽器是不認識v-bind(primaryColor);指令的,所以經(jīng)過編譯后就變成了瀏覽器認識的css變量var(--c845efc6-primaryColor);。

我們接著在elements面板中來看看此時class值為block的span元素,如下圖:

圖片圖片

從上圖中可以看到color的值為css變量var(--c845efc6-primaryColor),這個我們前面講過。不同的是這里從父級元素div中繼承過來一個--c845efc6-primaryColor: red;。

這個就是聲明一個名為--c845efc6-primaryColor的css變量,變量的值為red。

還記得我們在script模塊中定義的響應(yīng)式變量primaryColor嗎?他的值就是red。

所以這個span元素最終color渲染出來的值就是red。

接下來我們將通過debug的方式帶你搞清楚在style中是如何將指令v-bind(primaryColor)編譯成css變量var(--c845efc6-primaryColor),以及_useCssVars函數(shù)是如何生成聲明值為red的css變量--c845efc6-primaryColor。

doCompileStyle函數(shù)

在前面的文章中我們講過了style模塊實際是由doCompileStyle函數(shù)函數(shù)處理的,具體如何調(diào)用到doCompileStyle函數(shù)可以查看我之前的文章: 掉了兩根頭發(fā)后,我悟了!vue3的scoped原來是這樣避免樣式污染。

我們需要給doCompileStyle函數(shù)打個斷點,doCompileStyle函數(shù)的代碼位置在:node_modules/@vue/compiler-sfc/dist/compiler-sfc.cjs.js。

還是一樣的套路啟動一個debug終端。這里以vscode舉例,打開終端然后點擊終端中的+號旁邊的下拉箭頭,在下拉中點擊Javascript Debug Terminal就可以啟動一個debug終端。

圖片圖片

在debug終端執(zhí)行yarn dev,在瀏覽器中打開對應(yīng)的頁面,比如:http://localhost:5173/ 。

此時斷點將停留在doCompileStyle函數(shù)中,在我們這個場景中doCompileStyle函數(shù)簡化后的代碼如下:

import postcss from "postcss";

function doCompileStyle(options) {
  const {
    filename,
    id,
    postcssOptions,
    postcssPlugins,
  } = options;
  const source = options.source;
  const shortId = id.replace(/^data-v-/, "");

  const plugins = (postcssPlugins || []).slice();
  plugins.unshift(cssVarsPlugin({ id: shortId, isProd }));

  const postCSSOptions = {
    ...postcssOptions,
    to: filename,
    from: filename,
  };
  let result;
  try {
    result = postcss(plugins).process(source, postCSSOptions);
    return result.then((result) => ({
      code: result.css || "",
      // ...省略
    }));
  } catch (e: any) {
    errors.push(e);
  }
}

在前面的文章掉了兩根頭發(fā)后,我悟了!vue3的scoped原來是這樣避免樣式污染中我們講過了,這里id的值為使用了scoped后給html增加的自定義屬性data-v-x,每個vue文件生成的x都是不一樣的。在doCompileStyle函數(shù)中使用id.replace方法拿到x賦值給變量shortId。

接著就是定義一個plugins插件數(shù)組,并且將cssVarsPlugin函數(shù)的返回結(jié)果push進去。

這里cssVarsPlugin函數(shù)就是返回了一個自定義的postcss插件。

最后就是執(zhí)行result = postcss(plugins).process(source, postCSSOptions)拿到經(jīng)過postcss轉(zhuǎn)換編譯器處理后的css。

可能有的小伙伴對postcss不夠熟悉,我們這里來簡單介紹一下。

postcss 是 css 的 transpiler(轉(zhuǎn)換編譯器,簡稱轉(zhuǎn)譯器),它對于 css 就像 babel 對于 js 一樣,能夠做 css 代碼的分析和轉(zhuǎn)換。同時,它也提供了插件機制來做自定義的轉(zhuǎn)換。

在我們這里主要就是用到了postcss提供的插件機制來完成css scoped的自定義轉(zhuǎn)換,調(diào)用postcss的時候我們傳入了source,他的值是style模塊中的css代碼。并且傳入的plugins插件數(shù)組中有個cssVarsPlugin插件,這個自定義插件就是vue寫的用于處理在css中使用v-bind指令。

在執(zhí)行postcss對css代碼進行轉(zhuǎn)換之前我們在debug終端來看看此時的css代碼是什么樣的,如下圖:

sourcesource

從上圖中可以看到此時的options.source中還是v-bind(primaryColor)指令。

cssVarsPlugin插件

cssVarsPlugin插件在我們這個場景中簡化后的代碼如下:

const vBindRE = /v-bind\s*\(/g;
const cssVarsPlugin = (opts) => {
  const { id, isProd } = opts;
  return {
    postcssPlugin: "vue-sfc-vars",
    Declaration(decl) {
      const value = decl.value;
      if (vBindRE.test(value)) {
        vBindRE.lastIndex = 0;
        let transformed = "";
        let lastIndex = 0;
        let match;
        while ((match = vBindRE.exec(value))) {
          const start = match.index + match[0].length;
          const end = lexBinding(value, start);
          if (end !== null) {
            const variable = normalizeExpression(value.slice(start, end));
            transformed +=
              value.slice(lastIndex, match.index) +
              `var(--${genVarName(id, variable, isProd)})`;
            lastIndex = end + 1;
          }
        }
        decl.value = transformed + value.slice(lastIndex);
      }
    },
  };
};

這里的id就是我們在doCompileStyle函數(shù)中傳過來的shortId,每個vue文件對應(yīng)的shortId值都是不同的。

這里使用到了Declaration鉤子函數(shù),css中每個具體的樣式都會觸發(fā)這個Declaration鉤子函數(shù)。

給Declaration鉤子函數(shù)打個斷點,當post-css處理到color: v-bind(primaryColor);時就會走到這個斷點中。如下圖:

圖片圖片

將字符串v-bind(primaryColor)賦值給變量value,接著執(zhí)行if (vBindRE.test(value))。vBindRE是一個正則表達式,這里的意思是當前css的值是使用了v-bind指令才走到if語句里面。

接著就是執(zhí)行while ((match = vBindRE.exec(value)))進行正則表達式匹配,如果value的值符合vBindRE正則表達式,也就是value的值是v-bind綁定的,那么就走到while循環(huán)里面去。

看到這里有的小伙伴會問了,這里使用if就可以了,為什么還要使用while循環(huán)呢?

答案是css的值可能是多個v-bind指令組成的,比如border: v-bind(borderWidth) solid v-bind(primaryColor);。這里的css值就由兩個v-bind組成,分別是v-bind(borderWidth)和v-bind(primaryColor);。

為了處理上面這種多個v-bind指令組成的css值,所以就需要使用while循環(huán)搭配exec方法。正則表達式使用了global標志位的時候,js的RegExp 對象是有狀態(tài)的,它們會將上次成功匹配后的位置記錄在 lastIndex 屬性中。使用此特性,exec() 可用來對單個字符串中的多次匹配結(jié)果進行逐條的遍歷。

在debug終端來看看此時的match數(shù)組是什么樣的,如下圖:

圖片圖片

從上圖中可以看到match[0]的值是正則表達式匹配的字符串,在我們這里匹配的字符串是v-bind(。match.index的值為匹配到的字符位于原始字符串的基于 0 的索引值。

看到這里有的小伙伴可能對match.index的值有點不理解,我舉個簡單的例子你一下就明白了。

還是以v-bind(borderWidth) solid v-bind(primaryColor)為例,這個字符串就是原始字符串,第一次在while循環(huán)中正則表達式匹配到第一個bind,此時的match.index的值為0,也就是第一個v在原始字符串的位置。第二次在while循環(huán)中會基于第一次的位置接著向后找,會匹配到第二個v-bind指令,此時的match.index的值同樣也是基于原始字符串的位置,也就是第二個v-bind中的v的位置,值為26。

在while循環(huán)中使用const start = match.index + match[0].length給start變量賦值,match.index的值是v-bind中的v的位置。match[0]是正則匹配到的字符串 v-bind(。所以這個start的位置就是v-bind(primaryColor)中primaryColor變量的開始位置,也就是p所在的位置。

接著就是執(zhí)行l(wèi)exBinding函數(shù)拿到v-bind(primaryColor)中primaryColor變量的結(jié)束位置,賦值給變量end。在我們這個場景中簡化后的lexBinding函數(shù)代碼如下:

function lexBinding(content: string, start: number) {
  for (let i = start; i < content.length; i++) {
    const char = content.charAt(i);
    if (char === `)`) {
      return i;
    }
  }
  return null;
}

簡化后的lexBinding函數(shù)也很簡單,使用for循環(huán)遍歷v-bind(primaryColor)字符串,如果發(fā)現(xiàn)字符串)就說明找到了primaryColor變量的結(jié)束位置。

接著來看拿到end變量后的代碼,會執(zhí)行const variable = normalizeExpression(value.slice(start, end))。這里先執(zhí)行了value.slice(start, end)根據(jù)start開始位置和end結(jié)束位置提取出v-bind指令綁定的變量,接著normalizeExpression函數(shù)對其進行trim去除空格。

在我們這個場景中簡化后的normalizeExpression函數(shù)代碼如下:

function normalizeExpression(exp) {
  exp = exp.trim();
  return exp;
}

將從v-bind指令中提取出來的變量賦值給variable變量,接著執(zhí)行字符串拼接拿到由v-bind指令轉(zhuǎn)換成的css變量,代碼如下:

transformed +=
  value.slice(lastIndex, match.index) +
  `var(--${genVarName(id, variable, isProd)})`;

這里的value是css變量值v-bind(primaryColor),在我們這里lastIndex的值為0,match.index的值也是0,所以value.slice(lastIndex, match.index)拿到的值也是空字符串。

接著來看后面這部分,使用字符串拼接得到:var(--變量)。這個看著就很熟悉了,他就是一個css變量。變量名是調(diào)用genVarName函數(shù)生成的,genVarName函數(shù)代碼如下:

import hash from "hash-sum";
function genVarName(id, raw, isProd) {
  if (isProd) {
    return hash(id + raw);
  } else {
    return `${id}-${getEscapedCssVarName(raw)}`;
  }
}

這個id是根據(jù)當前vue組件路徑生成的,每個vue組件生成的id都不同。這個raw也就是綁定的響應(yīng)式變量,在這里是primaryColor。isProd表示當前是不是生產(chǎn)環(huán)境。

如果是生產(chǎn)環(huán)境就根據(jù)id和變量名使用哈希算法生成一個加密的字符串。

如果是開發(fā)環(huán)境就使用字符串拼接將id和變量名primaryColor拼接起來得到一個css變量。getEscapedCssVarName函數(shù)的代碼也很簡單,是對變量中的特殊字符進行轉(zhuǎn)義,以便在 CSS 變量名中使用。代碼如下:

const cssVarNameEscapeSymbolsRE = /[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g;
function getEscapedCssVarName(key: string) {
  return key.replace(cssVarNameEscapeSymbolsRE, (s) => `\\${s}`);
}

這也就是為什么不同組件的primaryColor生成的css變量名稱不會沖突的原因了,因為在生成的css變量前面拼接了一個id,每個vue組件生成的id值都不同。

拿到轉(zhuǎn)換成css變量的css值后,并且將其賦值給變量transformed。接著就是執(zhí)行l(wèi)astIndex = end + 1,在我們這里lastIndex就指向了字符串的末尾。

最后就是執(zhí)行decl.value = transformed + value.slice(lastIndex);將v-bind指令替換成css變量,由于lastIndex是指向了字符串的末尾,所以value.slice(lastIndex)的值也是一個空字符串。

所以在我們這里實際是執(zhí)行了decl.value = transformed,執(zhí)行完這句話后color的值就由v-bind(primaryColor)轉(zhuǎn)換成了var(--c845efc6-primaryColor)。

生成useCssVars函數(shù)

前面我們講過了編譯后的setup函數(shù)中多了一個useCssVars函數(shù),實際在我們的源代碼中是沒有這個useCssVars函數(shù)的。接下來我們來看看編譯時處理script模塊時是如何生成useCssVars函數(shù)的。

在之前的 為什么defineProps宏函數(shù)不需要從vue中import導(dǎo)入?文章中我們講過了vue的script模塊中的代碼是由compileScript函數(shù)處理的,當然你沒看過那篇文章也不影響這篇文章的閱讀。

給compileScript函數(shù)打個斷點,在我們這個場景中簡化后的compileScript函數(shù)代碼如下:

function compileScript(sfc, options) {
  const ctx = new ScriptCompileContext(sfc, options);
  const startOffset = ctx.startOffset;

  ctx.s.prependLeft(
    startOffset,
    `
${genCssVarsCode(sfc.cssVars, ctx.bindingMetadata, scopeId, !!options.isProd)}
`
  );
}

首先調(diào)用ScriptCompileContext類new了一個ctx上下文對象,我們這里來介紹一下需要使用到的ctx上下文對象中的兩個方法:ctx.s.toString、ctx.s.prependLeft。

  • ctx.s.toString:返回此時由script模塊編譯成的js代碼。
  • ctx.s.prependLeft:給編譯后的js代碼在指定index的前面插入字符串。

給ctx.s.prependLeft方法打個斷點,在debug終端使用ctx.s.toString方法來看看此時由script模塊編譯成的js代碼是什么樣的,如下圖:

圖片圖片

從上圖中可以看到此時生成的js代碼code字符串只有一條import語句和定義primaryColor變量。

由于篇幅有限我們就不深入到genCssVarsCode函數(shù)了,這個genCssVarsCode函數(shù)會生成useCssVars函數(shù)的調(diào)用。我們在debug終端來看看生成的code代碼字符串是什么樣的,如下圖:

圖片圖片

從上圖中可以看到genCssVarsCode函數(shù)生成了一個useCssVars函數(shù)。

執(zhí)行ctx.s.prependLeft函數(shù)后會將生成的useCssVars函數(shù)插入到生成的js code代碼字符串的前面,我們在debug終端來看看,如下圖:

圖片圖片

從上圖中可以看到此時的js code代碼字符串中已經(jīng)有了一個useCssVars函數(shù)了。

執(zhí)行useCssVars函數(shù)

前面我們講過了編譯時經(jīng)過cssVarsPlugin這個post-css插件處理后,v-bind(primaryColor)指令就會編譯成了css變量var(--c845efc6-primaryColor)。這里只是使用css變量值的地方,那么這個css變量的值又是在哪里定義的呢?答案是在useCssVars函數(shù)中。

在開始我們講過了編譯后的setup函數(shù)中多了一個useCssVars函數(shù),所以我們給useCssVars函數(shù)打個斷點,刷新瀏覽器此時代碼就會走到斷點中了。如下圖:

圖片圖片

從上圖中可以看到執(zhí)行useCssVars函數(shù)時傳入了一個回調(diào)函數(shù)作為參數(shù),這個回調(diào)函數(shù)返回了一個對象。

將斷點走進useCssVars函數(shù),在我們這個場景中簡化后的useCssVars函數(shù)代碼如下:

function useCssVars(getter) {
  const instance = getCurrentInstance();

  const setVars = () => {
    const vars = getter(instance.proxy);
    setVarsOnVNode(instance.subTree, vars);
  };

  watchPostEffect(setVars);
}

在useCssVars函數(shù)中先調(diào)用getCurrentInstance函數(shù)拿到當前的vue實例,然后將setVars函數(shù)作為參數(shù)傳入去執(zhí)行watchPostEffect函數(shù)。

這個watchPostEffect函數(shù)大家應(yīng)該知道,他是watchEffect() 使用 flush: 'post' 選項時的別名。

為什么需要使用 flush: 'post'呢?

答案是需要在setVars回調(diào)函數(shù)中需要去操作DOM,所以才需要使用 flush: 'post'讓回調(diào)函數(shù)在組件渲染完成之后去執(zhí)行。

給setVars函數(shù)打個斷點,組件渲染完成后斷點將會走進setVars函數(shù)中。

首先會執(zhí)行g(shù)etter函數(shù),將返回值賦值給變量vars。前面我們講過了這個getter函數(shù)是調(diào)用useCssVars函數(shù)時傳入的回調(diào)函數(shù),代碼如下:

_useCssVars((_ctx) => ({
  "c845efc6-primaryColor": primaryColor.value
}))

在這個回調(diào)函數(shù)中會返回一個對象,對象的key為c845efc6-primaryColor,這個key就是css變量var(--c845efc6-primaryColor)括號中的內(nèi)容。

對象的值是ref變量primaryColor的值,由于這個代碼是在watchPostEffect的回調(diào)函數(shù)中執(zhí)行的,所以這里的ref變量primaryColor也被作為依賴進行收集了。當primaryColor變量的值變化時,setVars函數(shù)也將再次執(zhí)行。這也就是為什么在style中可以使用v-bind指令綁定一個響應(yīng)式變量,并且當響應(yīng)式變量的值變化時樣式也會同步更新。

接著就是執(zhí)行setVarsOnVNode(instance.subTree, vars)函數(shù),傳入的第一個參數(shù)為instance.subTree。他的值是當前vue組件根元素的虛擬DOM,也就是根元素div的虛擬DOM。第二個參數(shù)為useCssVars傳入的回調(diào)函數(shù)返回的對象,這是一個css變量組成的對象。

接著將斷點走進setVarsOnVNode函數(shù),在我們這個場景中簡化后的代碼如下:

function setVarsOnVNode(vnode: VNode, vars) {
  setVarsOnNode(vnode.el, vars);
}

在setVarsOnVNode函數(shù)中是調(diào)用了setVarsOnNode函數(shù),不同的是傳入的第一個參數(shù)不再是虛擬DOM。而是vnode.el虛擬DOM對應(yīng)的真實DOM,也就是根節(jié)點div。

將斷點走進setVarsOnNode函數(shù),在我們這個場景中簡化后的setVarsOnNode函數(shù)代碼如下:

function setVarsOnNode(el: Node, vars) {
  if (el.nodeType === 1) {
    const style = el.style;
    for (const key in vars) {
      style.setProperty(`--${key}`, vars[key]);
    }
  }
}

在setVarsOnNode函數(shù)中先使用if語句判斷el.nodeType === 1,這個的意思是判斷當前節(jié)點類型是不是一個元素節(jié)點,比如<p>和<div>。如果是就走進if語句里面,使用el.style拿到根節(jié)點的style樣式。

這里的vars是css變量組成的對象,遍歷這個對象。對象的key為css變量名稱,對象的value為css變量的值。

接著就是遍歷css變量組成的對象,使用style.setProperty方法給根節(jié)點div增加內(nèi)聯(lián)樣式,也就是--c845efc6-primaryColor: red;。span元素由于是根節(jié)點div的子節(jié)點,所以他也繼承了樣式--c845efc6-primaryColor: red;。

由于span元素的color經(jīng)過編譯后已經(jīng)變成了css變量var(--c845efc6-primaryColor),并且從根節(jié)點繼承過來css變量--c845efc6-primaryColor的值為red,所以最終span元素的color值為red。

總結(jié)

下面這個是我總結(jié)的流程圖,如下(搭配流程圖后面的文字解釋一起服用效果最佳):

圖片圖片

編譯階段script模塊是由compileScript函數(shù)處理的,compileScript函數(shù)會去執(zhí)行一個genCssVarsCode函數(shù)。這個函數(shù)會返回一個useCssVars函數(shù)的調(diào)用。然后在compileScript函數(shù)中會調(diào)用ctx.s.prependLeft方法將生成的useCssVars函數(shù)插入到編譯后的setup函數(shù)中。

編譯階段style模塊是由doCompileStyle函數(shù)處理的,在doCompileStyle函數(shù)中會調(diào)用postcss對css樣式進行處理。vue自定義了一個名為cssVarsPlugin的postcss插件,插件中有個Declaration鉤子函數(shù),css中每個具體的樣式都會觸發(fā)這個Declaration鉤子函數(shù)。

在Declaration鉤子函數(shù)中使用正則表達式去匹配當前css值是不是v-bind綁定的,如果是就將匹配到的v-bind綁定的變量提取出來賦值給變量variable。還有一個id變量,他是根據(jù)當前vue組件的路徑生成的加密字符串。使用字符串拼接就可以得到var(--${id}-${variable}),他就是由v-bind編譯后生成的css變量。最終生成的css變量類似這樣:var(--c845efc6-primaryColor)。

運行時階段初始化的時候會去執(zhí)行setup函數(shù),由于在編譯階段setup函數(shù)中插入了一個useCssVars函數(shù)。使用在運行時階段初始化時useCssVars函數(shù)會被執(zhí)行。

在useCssVars函數(shù)中執(zhí)行了watchPostEffect函數(shù),他是watchEffect() 使用 flush: 'post' 選項時的別名。

由于我們需要在回調(diào)中操作DOM,所以才需要使用flush: 'post',讓回調(diào)函數(shù)在組件渲染之后去執(zhí)行。由于在回調(diào)函數(shù)中會去讀取v-bind綁定的響應(yīng)式變量,所以每次綁定的響應(yīng)式變量值變化后都會再次執(zhí)行調(diào)用watchPostEffect傳入的回調(diào)函數(shù),以此讓響應(yīng)式變量綁定的樣式保存更新。

在watchPostEffect傳入的回調(diào)函數(shù)中會通過當前vue組件實例拿到真實DOM的根節(jié)點,然后遍歷css變量組成的對象,將這些css變量逐個在根節(jié)點上面定義,類似這樣:--c845efc6-primaryColor: red;。由于css可以繼承,所以子節(jié)點都繼承了這個css定義。

我們的<span>標簽在編譯階段由color: v-bind(primaryColor);編譯成了css變量color: var(--c845efc6-primaryColor)。并且在運行時由于useCssVars函數(shù)的作用在根節(jié)點生成了css變量的定義--c845efc6-primaryColor: red;。由于css繼承,所以span標簽也繼承了這個css變量的定義,所以span標簽渲染到頁面上的color值最終為red。

責任編輯:武曉燕 來源: 前端歐陽
相關(guān)推薦

2021-12-02 05:50:35

Vue3 插件Vue應(yīng)用

2022-06-26 00:00:02

Vue3響應(yīng)式系統(tǒng)

2022-01-19 18:05:47

Vue3前端代碼

2025-07-31 09:05:38

2019-07-02 13:37:23

神經(jīng)網(wǎng)絡(luò)運算Python

2023-12-06 07:43:56

Vue如何定義事件

2021-09-27 06:29:47

Vue3 響應(yīng)式原理Vue應(yīng)用

2020-06-09 11:35:30

Vue 3響應(yīng)式前端

2025-07-31 09:01:07

2025-05-14 08:25:00

深度學(xué)習(xí)AI人工智能

2020-07-14 08:32:49

VuelocalStorag響應(yīng)式

2023-02-06 08:39:01

PreactVue3響應(yīng)式

2025-02-17 08:58:06

2024-08-13 09:26:07

2024-11-06 10:16:22

2023-11-28 09:03:59

Vue.jsJavaScript

2024-02-01 09:10:04

頁面引導(dǎo)工具Vue3

2022-12-06 08:39:27

Vue3Reactive

2024-07-12 08:56:40

2015-10-22 09:05:12

點贊
收藏

51CTO技術(shù)棧公眾號

欧美日韩国产精品一卡| 国产精品久久久久久影院8一贰佰 国产精品久久久久久麻豆一区软件 | 精品国产一区二区三区久久影院| 久久久久网址| 紧身裙女教师波多野结衣| 西西人体44www大胆无码| 欧美国产偷国产精品三区| 姬川优奈aav一区二区| 国产一区二区中文字幕| 国产人伦精品一区二区| 国产做受高潮69| 日本成人在线免费观看| 最新真实国产在线视频| 香蕉久久精品日日躁夜夜躁| 一区二区三区欧美| 91精品久久久久久久久不口人| 三上悠亚影音先锋| 91九色porn在线资源| 国产精品原创巨作av| 日韩小视频在线观看| 日韩有码免费视频| 午夜国产在线视频| 美女mm1313爽爽久久久蜜臀| 这里只有精品丝袜| 精品国产免费av| 神马午夜一区二区| 亚洲三级视频| 日韩经典中文字幕| 日本在线观看a| 九色网友自拍视频手机在线| 久久精品网址| 在线丨暗呦小u女国产精品| 亚洲 自拍 另类 欧美 丝袜| 日本美女在线中文版| 精品在线你懂的| 欧美精品一区三区| 国产精久久久久| 日本不卡免费高清视频在线| 久久久一区二区三区| 人九九综合九九宗合| 亚洲精品色午夜无码专区日韩| 日本综合字幕| 中文字幕不卡的av| 91精品一区二区| 久久香蕉精品视频| 亚洲第一论坛sis| 欧美综合一区二区| 国产精品波多野结衣| www.爱爱.com| 另类天堂av| 亚洲2020天天堂在线观看| 亚洲av网址在线| av激情成人网| 一区二区国产视频| 99re99热| 在线观看午夜av| 成人av电影在线播放| 日本午夜人人精品| 成人一级黄色大片| 国产成人av毛片| 91成人国产精品| 精品www久久久久奶水| 成全电影大全在线观看| 久久久av毛片精品| 欧美在线视频二区| 国产欧美第一页| 日韩午夜av| 久久天天躁狠狠躁夜夜躁| 99久久人妻无码中文字幕系列| 成人福利网站在线观看| 一出一进一爽一粗一大视频| 成人线上播放| 欧美日韩精品专区| 亚洲熟妇无码另类久久久| av免费观看一区二区| 国产女主播一区| 一区二区精品免费视频| 亚洲 欧美 自拍偷拍| 久久久久久毛片| 日韩伦理一区二区三区av在线| 亚洲黄色a级片| 免费观看日韩av| 久久久久久午夜| 日韩一卡二卡在线观看| 亚洲性视频大全| 在线观看日韩专区| 中文成人无字幕乱码精品区| 另类一区二区| 午夜精品一区二区三区免费视频| 在线观看成人av| 户外极限露出调教在线视频| 国产日韩欧美精品综合| 国产伦精品一区二区三毛| 亚洲手机在线观看| 亚洲永久网站| 992tv成人免费视频| 欧美成人片在线观看| 日韩欧美1区| 亚洲欧美综合另类中字| 国产麻豆剧传媒精品国产av| 九九精品在线| 日韩精品在线免费播放| 性欧美18—19sex性高清| av日韩在线免费观看| 欧美视频一区二| 日本男人操女人| 美女100%一区| 欧美性高潮在线| 欧美 日韩 亚洲 一区| 国产一区二区三区朝在线观看| 欧美一区二区三区视频在线| 日本一二区免费| 久久av日韩| 精品国产乱子伦一区| 亚洲国产天堂av| 欧美日韩专区| 久久久久久久色| 91成年人视频| 国产一区美女在线| 蜜桃久久精品乱码一区二区| 欧洲精品久久一区二区| 国产精品亚洲一区二区三区妖精| 快播日韩欧美| 日本中文字幕中出在线| 夜夜爽夜夜爽精品视频| 99视频在线免费| 欧美美女在线直播| 亚洲色图日韩av| 国产在线视频二区| 9国产精品视频| 亚洲伊人久久综合| 亚洲精品综合久久| 国产精品电影院| 神马午夜伦理影院| 黑人玩欧美人三根一起进| 欧美色网站导航| www激情五月| 超碰在线成人| 久久福利视频导航| 91精品国产综合久| 欧美激情一区二区三区在线| 免费欧美一级视频| 狼人精品一区二区三区在线| 精品在线观看国产| 日韩黄色一级大片| 日本在线不卡一区| 97se在线视频| 日本1级在线| 中文字幕日韩欧美一区二区三区| 欧美一区二区三区综合| 中文字幕 在线观看| 欧美日韩久久久一区| 日韩精品电影一区二区三区| 伊人成综合网| 欧美最猛性xxxxx免费| 日本人妻熟妇久久久久久| 亚洲成人动漫一区| 亚洲一级片免费| 大奶在线精品| 久久久免费av| 无码精品视频一区二区三区 | 欧美成人一级视频| 你懂的在线观看网站| 伊人久久大香线蕉综合热线| 国产国产精品人在线视| 精品国产免费无码久久久| 日韩毛片视频在线看| 久久精品亚洲天堂| 欧美88av| 国产精品中文字幕在线| 日本成人动漫在线观看| 天天做天天摸天天爽国产一区 | 91国产精品一区| 综合婷婷亚洲小说| 国产成人精品一区二区三区在线观看| 亚洲黄色天堂| 日韩精品伦理第一区| 欧美日韩va| 欧美极品少妇xxxxⅹ裸体艺术| 亚洲av成人精品毛片| 欧美性受xxxx| 欧美日韩在线观看免费| 99国产精品久久| 国产一级片91| 女同久久另类99精品国产| 日韩av电影院| 国产网站在线免费观看| 欧美日韩在线第一页| 日韩大尺度视频| 亚洲欧美高清| 天天做天天爱天天高潮| 欧美freesex8一10精品| 国产欧洲精品视频| 国产探花视频在线观看| 亚洲色图第三页| 国产极品久久久| 亚洲图片激情小说| 亚洲综合欧美在线| 国产日产精品一区二区三区四区的观看方式 | 国产日韩欧美在线观看| 欧美videossex| 欧美一区二区视频观看视频| 国产精品第108页| 国产精品网站一区| 亚洲精品国产成人av在线| 免费成人在线观看视频| 亚洲人成无码网站久久99热国产| 欧美国产中文高清| 日韩在线观看免费全| 日本免费不卡视频| 884aa四虎影成人精品一区| 男人的天堂官网| 日本美女一区二区| av在线免费观看国产| www.神马久久| 国产日本欧美在线观看| 女人让男人操自己视频在线观看| 精品国产一区二区三区在线观看| 免费黄色片视频| 国产亚洲精久久久久久| 亚洲欧洲日韩综合| 蓝色福利精品导航| 久久久精品在线视频| 亚洲午夜视频| 九色91视频| 国产精品视频一区二区三区综合 | 制服丝袜亚洲网站| 久久精品视频7| 久久综合狠狠综合久久综合88| jizzjizz国产精品喷水| 精品在线播放| 久久国产精品 国产精品| 综合中文字幕| 欧美一级电影在线| 电影av在线| 7777精品伊人久久久大香线蕉的 | 特级西西人体高清大胆| 狠狠色狠狠色综合系列| 免费看黄色a级片| 国产精品91一区二区三区| 视频二区一区| 久久av网站| 国产日韩欧美自拍| 国产69精品久久| 欧美极品少妇xxxxⅹ免费视频| 成a人片在线观看| 亚洲精品99久久久久| 18国产免费视频| 一区二区在线电影| 国产 中文 字幕 日韩 在线| 国产成人午夜精品影院观看视频| 九一国产精品视频| 精品二区久久| 自拍日韩亚洲一区在线| 日韩亚洲国产精品| 波多野结衣家庭教师在线| 99在线观看免费视频精品观看| 日本欧美黄色片| 久久在线精品| 黄色a级片免费看| 精品成人免费| 成年人免费在线播放| 老牛嫩草一区二区三区日本| 国产v亚洲v天堂无码久久久| 青青草国产精品97视觉盛宴| 亚洲综合色在线观看| 狠狠色狠狠色综合日日91app| 999热精品视频| 成人精品国产福利| 国产三级国产精品| 国产日韩欧美电影| 强制高潮抽搐sm调教高h| 亚洲精品成人少妇| 国偷自拍第113页| 一区二区三区欧美激情| 久草视频中文在线| 国产精品卡一卡二| 亚洲精品视频久久久| 国产午夜亚洲精品午夜鲁丝片 | 国产精品高潮呻吟视频| 欧美女同一区| 欧美在线视频观看| 精品国产黄a∨片高清在线| 51国偷自产一区二区三区| 国产精品一线| 日韩免费三级| 国产精品二区影院| av免费在线播放网站| 九九视频精品免费| 国产性生活毛片| 国产精品青草久久| 九九九免费视频| 日本道色综合久久| 亚洲午夜18毛片在线看| 亚洲电影在线免费观看| 老熟妇仑乱一区二区av| 欧美高清你懂得| 中文字幕av资源| 欧洲在线/亚洲| 国产不卡精品视频| 亚洲人成电影网站色…| 51xtv成人影院| 国产成人+综合亚洲+天堂| 日本精品一区二区三区在线观看视频| 国产日韩在线免费| 国产欧美自拍一区| 一区二区三区视频| 国产精品日韩欧美一区| 无码播放一区二区三区| 国产自产v一区二区三区c| 国产伦精品一区三区精东| 国产精品成人一区二区三区夜夜夜| 国产亚洲欧美久久久久| 欧美三级一区二区| 五月天婷婷在线播放| 欧美猛少妇色xxxxx| 成人在线免费看片| 国产suv精品一区二区| 成人午夜三级| 欧洲精品视频在线| 麻豆国产欧美日韩综合精品二区 | 国产在视频线精品视频www666| 一本色道久久88亚洲精品综合 | 91精品美女在线| 国产精品免费99久久久| 亚洲美免无码中文字幕在线 | 亚洲永久精品ww.7491进入| 亚洲主播在线播放| 国产伦一区二区| 中文字幕日韩精品在线| 345成人影院| 久久艳妇乳肉豪妇荡乳av| 激情亚洲成人| 又大又长粗又爽又黄少妇视频| |精品福利一区二区三区| 中文字幕一区二区人妻痴汉电车| 亚洲欧美成人一区二区在线电影| 国产一级二级三级在线观看| 久久人人爽人人爽人人片av高请| 国产va免费精品观看精品| 一区精品在线| 九九热在线视频观看这里只有精品| 欧美大波大乳巨大乳| 色94色欧美sute亚洲线路一ni| 日本中文字幕电影在线观看 | 精品国产乱码一区二区| 精品国产一区二区三区av性色 | 日本道不卡免费一区| 亚洲免费av网| 久久99精品视频| 免费成人深夜夜行网站| 91麻豆精品91久久久久同性| 黄色在线观看网站| 亚洲在线www| 国产精品第十页| 日本黄色动态图| 欧美性猛交xxxx免费看漫画| 九色在线免费| 91精品久久久久| 欧美日韩亚洲一区三区| 欧美一级片黄色| 欧美日韩综合视频| 国产有码在线| 成人黄色短视频在线观看| 中文精品久久| 白嫩情侣偷拍呻吟刺激| 欧美日韩亚洲精品内裤| 岛国大片在线观看| 美女福利精品视频| 日韩视频一二区| 91专区在线观看| 国产视频一区二区在线| 一区二区日韩视频| 欧美激情视频一区二区| 欧美高清你懂的| 国产午夜精品视频一区二区三区| 大桥未久av一区二区三区中文| 国产精品久久国产精麻豆96堂| 欧美区视频在线观看| 视频在线这里都是精品| 黄色91av| 国产一区二区三区自拍| 艳妇乳肉亭妇荡乳av| 欧美中文字幕一区二区三区| 免费看a在线观看| 国产精品国内视频| 久久久久久久久国产一区| 欧美一级特黄a| 亚洲精品国产精华液| 天堂а在线中文在线无限看推荐| 欧美激情a在线| 啪啪亚洲精品| 真实乱偷全部视频| 一本大道久久a久久精品综合| 手机看片福利在线| 国产91精品在线播放| 一区二区三区四区在线观看国产日韩| 800av在线播放| 欧美日韩国产综合草草| 动漫一区二区|