国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频

從 0 搭建 Vite 3 + Vue 3 前端工程化項(xiàng)目

共 44432字,需瀏覽 89分鐘

 ·

2022-11-17 11:17

前言

Vue 3 正式版已經(jīng)發(fā)布有一段時(shí)間了,隨著 Vite 腳手架注定成為下一代前端工具鏈,許多用戶都想基于 Vite 來構(gòu)建 Vue 項(xiàng)目,如果想基于 Vite 構(gòu)建 Vue 3 項(xiàng)目,社區(qū)模板完全滿足您的需求,如果想構(gòu)建 Vite 3 + Vue 3 + JavaScript 項(xiàng)目,那社區(qū)模板不太能滿足您的需求,因?yàn)樯鐓^(qū)模板提供 Vue 3 項(xiàng)目幾乎是基于 Vite 2 + TypeScript 構(gòu)建,對(duì)于不熟悉 TypeScript 語言的用戶不是很友好,因此接下來從 0 開始手把手帶大家搭建一套規(guī)范的 Vite 3 + Vue 3 + JavaScript 前端工程化項(xiàng)目環(huán)境。

本文章篇幅較長(zhǎng),從以下幾個(gè)方面展開:

  • 基礎(chǔ)搭建
  • 代碼規(guī)范
  • 提交規(guī)范
  • 自動(dòng)部署

本項(xiàng)目完整代碼托管在 GitHub 倉(cāng)庫(kù)[1],歡迎點(diǎn)亮小星星 ????

技術(shù)棧

  • ?? Vite 3[2] - 構(gòu)建工具(就是快?。?/section>
  • ?? Vue 3[3] - 漸進(jìn)式 JavaScript 框架
  • ?? Vue Router[4] - 官方路由管理器
  • ?? Pinia[5] - 值得你喜歡的 Vue Store
  • ?? TDesign[6] - TDesign 適配桌面端的組件庫(kù)
  • ?? Less[7] - CSS 預(yù)處理器
  • ?? Axios[8] - 一個(gè)基于 promise 的網(wǎng)絡(luò)請(qǐng)求庫(kù),可以用于瀏覽器和 node.js
  • ?? Husky[9] + Lint-Staged[10] - Git Hook 工具
  • ??? EditorConfig[11] + ESLint[12] + Prettier[13] + Stylelint[14] - 代碼規(guī)范
  • ?? Commitizen[15] + Commitlint[16] - 提交規(guī)范
  • ?? GitHub Actions[17] - 自動(dòng)部署

基礎(chǔ)搭建

構(gòu)建項(xiàng)目雛形

確保你安裝了最新版本的 Node.js[18],然后在命令行中運(yùn)行以下命令:

# npm 6.x
npm create vite@latest vite-vue-js-template --template vue

# npm 7+, extra double-dash is needed:
npm create vite@latest vite-vue-js-template -- --template vue

# yarn
yarn create vite vite-vue-js-template --template vue

# pnpm
pnpm create vite vite-vue-js-template --template vue
復(fù)制代碼

這一指令將會(huì)安裝并執(zhí)行 create-vite[19],它是一個(gè)基本模板快速啟動(dòng)項(xiàng)目工具。

初始化項(xiàng)目

在項(xiàng)目被創(chuàng)建后,通過以下步驟安裝依賴并啟動(dòng)開發(fā)服務(wù)器:

# 打開項(xiàng)目
cd <your-project-name>

# 安裝依賴
npm install

# 啟動(dòng)項(xiàng)目
npm run dev
復(fù)制代碼
啟動(dòng)項(xiàng)目

Vite 基礎(chǔ)配置

Vite 配置文件 vite.config.js 位于項(xiàng)目根目錄下,項(xiàng)目啟動(dòng)時(shí)會(huì)自動(dòng)讀取。

本項(xiàng)目針對(duì)公共基礎(chǔ)路徑、自定義路徑別名、服務(wù)器選項(xiàng)、構(gòu)建選項(xiàng)等做了如下基礎(chǔ)配置:

import { defineConfig } from 'vite';
import { resolve } from 'path';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
    base'./',
    plugins: [
      vue(),
    ],
    resolve: {
      alias: {
        '@': resolve(__dirname, './src') ,
      },
    },
    server: {
      // 是否開啟 https
      httpsfalse,
      // 端口號(hào)
      port3000,
      // 監(jiān)聽所有地址
      host'0.0.0.0',
      // 服務(wù)啟動(dòng)時(shí)是否自動(dòng)打開瀏覽器
      opentrue,
      // 允許跨域
      corstrue,
      // 自定義代理規(guī)則
      proxy: {},
    },
    build: {
      // 設(shè)置最終構(gòu)建的瀏覽器兼容目標(biāo)
      target'es2015',
      // 構(gòu)建后是否生成 source map 文件
      sourcemapfalse,
      //  chunk 大小警告的限制(以 kbs 為單位)
      chunkSizeWarningLimit2000,
      // 啟用/禁用 gzip 壓縮大小報(bào)告
      reportCompressedSizefalse,
    },
});
復(fù)制代碼

關(guān)于 Vite 更多配置項(xiàng)及用法,請(qǐng)查看 Vite 官網(wǎng) vitejs.dev/config/[20]

規(guī)范目錄結(jié)構(gòu)

├── dist/
└── src/
├── api/ // 接口請(qǐng)求目錄
├── assets/ // 靜態(tài)資源目錄
├── common/ // 通用類庫(kù)目錄
├── components/ // 公共組件目錄
├── router/ // 路由配置目錄
├── store/ // 狀態(tài)管理目錄
├── style/ // 通用樣式目錄
├── utils/ // 工具函數(shù)目錄
├── views/ // 頁面組件目錄
├── App.vue
├── main.js
├── tests/ // 單元測(cè)試目錄
├── index.html
├── jsconfig.json // JavaScript 配置文件
├── vite.config.js // Vite 配置文件
└── package.json
復(fù)制代碼

集成 Vue Router 路由工具

安裝依賴

npm i vue-router@4
復(fù)制代碼

創(chuàng)建路由配置文件

src/router 目錄下新建 index.js 文件與 modules 文件夾

└── src/
├── router/
├── modules/ // 路由模塊
├── index.js // 路由配置文件
復(fù)制代碼

關(guān)于路由表,建議根據(jù)功能的不同來拆分到 modules 文件夾中,好處是:

  • 方便后期維護(hù)

    • 減少 Git 合并代碼沖突可能性
export default [
  {
    path'/',
    name'home',
    component() => import('@/views/HomeView.vue'),
  },
  {
    path'/about',
    name'about',
    component() => import('@/views/AboutView.vue'),
  },
];
復(fù)制代碼
import { createRouter, createWebHistory } from 'vue-router';

import baseRouters from './modules/base';

const routes = [...baseRouters];

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes,
  scrollBehavior() {
    return {
      el'#app',
      top0,
      behavior'smooth',
    };
  },
});

export default router;
復(fù)制代碼

根據(jù)路由配置的實(shí)際情況,需要在 src 下創(chuàng)建 views 目錄,用來存儲(chǔ)頁面組件。

掛載路由配置

main.js 文件中掛載路由配置

import { createApp } from 'vue';

import App from './App.vue';
import router from './router';

createApp(App).use(router).mount('#app');
復(fù)制代碼

集成 Pinia 全局狀態(tài)管理工具

安裝依賴

npm i pinia
復(fù)制代碼

創(chuàng)建倉(cāng)庫(kù)配置文件

src/store 目錄下新建 index.js 文件與 modules 文件夾

└── src/
├── store/
├── modules/ // 倉(cāng)庫(kù)模塊
├── index.js // 倉(cāng)庫(kù)配置文件
復(fù)制代碼
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
  state() => ({
    count1,
  }),
  actions: {
    accumulate() {
      this.count++;
    },
  },
});
復(fù)制代碼
import { createPinia } from 'pinia';

const store = createPinia();

export default store;

export * from './modules/counter';
復(fù)制代碼

開發(fā)中需要將不同功能所對(duì)應(yīng)的狀態(tài),拆分到不同的 modules,好處如同路由模塊一樣。

掛載 Pinia 配置

main.js 文件中掛載 Vuex 配置

import { createApp } from 'vue';

import App from './App.vue';
import store from './store';
import router from './router';

createApp(App).use(router).use(store).mount('#app');
復(fù)制代碼

集成 TDesign Vue Next 組件庫(kù)

安裝依賴

npm i tdesign-vue-next
復(fù)制代碼

基礎(chǔ)使用

import { createApp } from 'vue';

import TDesign from 'tdesign-vue-next';

// 引入組件庫(kù)全局樣式資源
import 'tdesign-vue-next/es/style/index.css';

const app = createApp(App);
app.use(TDesign);
復(fù)制代碼

按需引入

使用 unplugin-vue-componentsunplugin-auto-import 來實(shí)現(xiàn)自動(dòng)導(dǎo)入:

npm install unplugin-vue-components unplugin-auto-import -D
復(fù)制代碼

在 Vite 對(duì)應(yīng)的配置文件 vite.config.js 添加上述插件:

import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { TDesignResolver } from 'unplugin-vue-components/resolvers';

export default {
  plugins: [
    AutoImport({
      resolvers: [TDesignResolver({
        library'vue-next'
      })],
    }),
    Components({
      resolvers: [TDesignResolver({
        library'vue-next'
      })],
    }),
  ],
};
復(fù)制代碼

集成 Axios HTTP 工具

安裝依賴

npm i axios
復(fù)制代碼

請(qǐng)求配置

utils 目錄下創(chuàng)建 request.js 文件,配置好適合自己業(yè)務(wù)的請(qǐng)求攔截和響應(yīng)攔截:

└── src/
├── api // 接口
├── utils/
├── request.js // axios 請(qǐng)求庫(kù)二次封裝
復(fù)制代碼
import axios from 'axios';

// 創(chuàng)建請(qǐng)求實(shí)例
const instance = axios.create({
  baseURL'/api',
  // 指定請(qǐng)求超時(shí)的毫秒數(shù)
  timeout1000,
  // 表示跨域請(qǐng)求時(shí)是否需要使用憑證
  withCredentialsfalse,
});

// 前置攔截器(發(fā)起請(qǐng)求之前的攔截)
instance.interceptors.request.use(
  (config) => {
    /**
     * 在這里一般會(huì)攜帶前臺(tái)的參數(shù)發(fā)送給后臺(tái),比如下面這段代碼:
     * const token = getToken()
     * if (token) {
     *  config.headers.token = token
     * }
     */

    return config;
  },
  (error) => {
    return Promise.reject(error);
  },
);

// 后置攔截器(獲取到響應(yīng)時(shí)的攔截)
instance.interceptors.response.use(
  (response) => {
    /**
     * 根據(jù)你的項(xiàng)目實(shí)際情況來對(duì) response 和 error 做處理
     * 這里對(duì) response 和 error 不做任何處理,直接返回
     */

    return response;
  },
  (error) => {
    const { response } = error;
    if (response && response.data) {
      return Promise.reject(error);
    }
    const { message } = error;
    console.error(message);
    return Promise.reject(error);
  },
);

// 導(dǎo)出常用函數(shù)

/**
 * @param {string} url
 * @param {object} data
 * @param {object} params
 */

export function post(url, data = {}, params = {}{
  return instance({
    method'post',
    url,
    data,
    params,
  });
}

/**
 * @param {string} url
 * @param {object} params
 */

export function get(url, params = {}{
  return instance({
    method'get',
    url,
    params,
  });
}

/**
 * @param {string} url
 * @param {object} data
 * @param {object} params
 */

export function put(url, data = {}, params = {}{
  return instance({
    method'put',
    url,
    params,
    data,
  });
}

/**
 * @param {string} url
 * @param {object} params
 */

export function _delete(url, params = {}{
  return instance({
    method'delete',
    url,
    params,
  });
}

export default instance;
復(fù)制代碼

之后在 api 文件夾中以業(yè)務(wù)模型對(duì)接口進(jìn)行拆分,舉個(gè)例子,將所有跟用戶相關(guān)接口封裝在 User 類中,此類稱作用戶模型。

User 類中比如有登錄、注冊(cè)、獲取用戶信息等方法,如果有業(yè)務(wù)邏輯變動(dòng),只需要修改相關(guān)方法即可。

import { post } from '@/utils/request';

export default class User {
  /**
   * 登錄
   * @param {String} username 用戶名
   * @param {String} password 密碼
   * @returns
   */

  static async login(username, password) {
    return post('/login', {
      username,
      password,
    });
  }
}
復(fù)制代碼

把每個(gè)業(yè)務(wù)模型獨(dú)立成一個(gè) js 文件,聲明一個(gè)類通過其屬性和方法來實(shí)現(xiàn)這個(gè)模型相關(guān)的數(shù)據(jù)獲取,這樣可以大大提升代碼的可讀性與可維護(hù)性。

模擬演示

在需要使用接口的地方,引入對(duì)應(yīng)的業(yè)務(wù)模型文件,參考如下:

<script>
import User from '@/api/user';

export default {
  data() {
    return {
      username'',
      password'',
    };
  },
  methods: {
    async login() {
      const res = await User.login(this.username, this.password);
      console.log(res);
    },
  },
};
</script>

復(fù)制代碼

集成 CSS 預(yù)處理器 Less

本項(xiàng)目使用 CSS 預(yù)處理器 Less,直接安裝為開發(fā)依賴即可。

Vite 內(nèi)部已幫我們集成了相關(guān)的 loader,不需要額外配置。

安裝依賴

npm i less -D
復(fù)制代碼

如何使用

<style></style> 樣式標(biāo)簽中引用 lang="less" 即可。

<style lang="less"></style>
復(fù)制代碼

CSS 命名規(guī)范推薦 BEM 命名規(guī)范

參考鏈接:CSS BEM 書寫規(guī)范[21]

全局樣式

src/style 目錄下創(chuàng)建 variables.less 全局樣式文件:

└── src/
├── style/
├── variables.less // 全局樣式文件
復(fù)制代碼

vite.config.js 配置文件中新增CSS 預(yù)處理器相關(guān)配置即可實(shí)現(xiàn) less 全局樣式:

import { resolve } from 'path';

export default defineConfig({
    css: {
      preprocessorOptions: {
        less: {
          modifyVars: {
            hack`true; @import (reference) "${resolve('src/style/variables.less')}";`,
          },
          math'strict',
          javascriptEnabledtrue,
        },
      },
    },
});
復(fù)制代碼

樣式穿透

官方文檔[22]

在 Vue3 中,改變了以往樣式穿透的語法,如果繼續(xù)使用 ::v-deep/deep/、>>> 等語法的話,會(huì)出現(xiàn)一個(gè)警告,下面是新的語法:

/* 深度選擇器 */
:deep(selector) {
  /* ... */
}

/* 插槽選擇器 */
:slotted(selector) {
  /* ... */
}

/* 全局選擇器 */
:global(selector) {
  /* ... */
}
復(fù)制代碼

至此,一個(gè)基于 JavaScript + Vite3 + Vue3 + Vue Router + Pinia + Axios + Less 的前端項(xiàng)目開發(fā)環(huán)境搭建完畢。

項(xiàng)目托管在 GitHub 倉(cāng)庫(kù)[23],需要的同學(xué)可以去下載下來,參考學(xué)習(xí)。

接下來增加代碼規(guī)范約束、提交規(guī)范約束、單元測(cè)試、自動(dòng)部署等,讓其更完善、更健壯。

代碼規(guī)范

隨著前端應(yīng)用逐漸變得大型化和復(fù)雜化,在同一個(gè)項(xiàng)目中有多個(gè)人員參與時(shí),每個(gè)人的前端能力程度不等,他們往往會(huì)用不同的編碼風(fēng)格和習(xí)慣在項(xiàng)目中寫代碼,長(zhǎng)此下去,勢(shì)必會(huì)讓項(xiàng)目的健壯性越來越差。解決這些問題,理論上講,口頭約定和代碼審查都可以,但是這種方式無法實(shí)時(shí)反饋,而且溝通成本過高,不夠靈活,更關(guān)鍵的是無法把控。不以規(guī)矩,不能成方圓,我們不得不在項(xiàng)目使用一些工具來約束代碼規(guī)范。

本文講解如何使用 EditorConfig + ESLint + Prettier + Stylelint 組合來實(shí)現(xiàn)代碼規(guī)范化。

這樣做帶來好處:

  • 解決團(tuán)隊(duì)之間代碼不規(guī)范導(dǎo)致的可讀性差和可維護(hù)性差的問題。
  • 解決團(tuán)隊(duì)成員不同編輯器導(dǎo)致的編碼規(guī)范不統(tǒng)一問題。
  • 提前發(fā)現(xiàn)代碼風(fēng)格問題,給出對(duì)應(yīng)規(guī)范提示,及時(shí)修復(fù)。
  • 減少代碼審查過程中反反復(fù)復(fù)的修改過程,節(jié)約時(shí)間。
  • 自動(dòng)格式化,統(tǒng)一編碼風(fēng)格,從此和臟亂差的代碼說再見。

集成 EditorConfig 配置

EditorConfig[24] 主要用于統(tǒng)一不同 IDE 編輯器的編碼風(fēng)格。

在項(xiàng)目根目錄下添加 .editorconfig 文件:

# 表示是最頂層的 EditorConfig 配置文件
root = true

# 表示所有文件適用
[*]
# 縮進(jìn)風(fēng)格(tab | space)
indent_style = space
# 控制換行類型(lf | cr | crlf)
end_of_line = lf
# 設(shè)置文件字符集為 utf-8
charset = utf-8
# 去除行首的任意空白字符
trim_trailing_whitespace = true
# 始終在文件末尾插入一個(gè)新行
insert_final_newline = true

# 表示僅 md 文件適用以下規(guī)則
[*.md]
max_line_length = off
trim_trailing_whitespace = false

# 表示僅 ts、js、vue、css 文件適用以下規(guī)則
[*.{ts,js,vue,css}]
indent_size = 2
復(fù)制代碼

很多 IDE 中會(huì)默認(rèn)支持此配置,但是也有些不支持,如:VSCode、Atom、Sublime Text 等。

具體列表可以參考官網(wǎng),如果在 VSCode 中使用需要安裝 EditorConfig for VS Code 插件。

EditorConfig for VS Code

集成 ESLint 配置

ESLint[25] 是針對(duì) EScript 的一款代碼檢測(cè)工具,它可以檢測(cè)項(xiàng)目中編寫不規(guī)范的代碼,如果寫出不符合規(guī)范的代碼會(huì)被警告。

由此我們就可以借助于 ESLint 強(qiáng)大的功能來統(tǒng)一團(tuán)隊(duì)的編碼規(guī)范。

安裝依賴

  • `ESLint`[26] - ESLint 本體
  • `eslint-define-config`[27] - 改善 ESLint 規(guī)范編寫體驗(yàn)
  • `eslint-plugin-vue`[28] - 適用于 Vue 文件的 ESLint 插件
  • `eslint-config-airbnb-base`[29] - Airbnb JavaScript 風(fēng)格指南
  • `eslint-plugin-import`[30] - 使用 eslint-config-airbnb-base 時(shí)必須安裝的前置插件
  • `vue-eslint-parser`[31] - 使用 eslint-plugin-vue 時(shí)必須安裝的 ESLint 解析器
npm i eslint eslint-define-config eslint-config-airbnb-base eslint-plugin-import eslint-plugin-vue vue-eslint-parser -D
復(fù)制代碼

安裝插件

Visual Studio Code 編輯器使用 ESLint 配置需要下載插件 ESLint 。

ESLint

JetBrains 系列編輯器(WebStorm、IntelliJ IDEA 等)則不用額外安裝插件。

創(chuàng)建 ESLint 配置文件

在項(xiàng)目根目錄創(chuàng)建 .eslintrc.js 文件,并填入以下內(nèi)容:

const { defineConfig } = require('eslint-define-config');

module.exports = defineConfig({
  roottrue,
  env: {
    browsertrue,
    nodetrue,
    jesttrue,
    es6true,
  },
  plugins: ['vue'],
  parser'vue-eslint-parser',
  parserOptions: {
    ecmaVersion'latest',
    sourceType'module',
    allowImportExportEverywheretrue,
    ecmaFeatures: {
      jsxtrue,
    },
  },
  extends: [
    'airbnb-base',
    'eslint:recommended',
    'plugin:vue/vue3-essential',
    'plugin:vue/vue3-recommended',
    'plugin:prettier/recommended',
  ],
  rules: {
    // 禁止使用多余的包
    'import/no-extraneous-dependencies'0,
    // 確保在導(dǎo)入路徑內(nèi)一致使用文件擴(kuò)展名
    'import/extensions'0,
    // 確保導(dǎo)入指向可以解析的文件/模塊
    'import/no-unresolved'0,
    // 首選默認(rèn)導(dǎo)出導(dǎo)入/首選默認(rèn)導(dǎo)出
    'import/prefer-default-export'0,
    // 要求使用 let 或 const 而不是 var
    'no-var''error',
    // 禁止使用 new 以避免產(chǎn)生副作用
    'no-new'1,
    // 禁止變量聲明與外層作用域的變量同名
    'no-shadow'0,
    // 禁用 console
    'no-console'0,
    // 禁止標(biāo)識(shí)符中有懸空下劃線
    'no-underscore-dangle'0,
    // 禁止在可能與比較操作符相混淆的地方使用箭頭函數(shù)
    'no-confusing-arrow'0,
    // 禁用一元操作符 ++ 和 --
    'no-plusplus'0,
    // 禁止對(duì) function 的參數(shù)進(jìn)行重新賦值
    'no-param-reassign'0,
    // 禁用特定的語法
    'no-restricted-syntax'0,
    // 禁止在變量定義之前使用它們
    'no-use-before-define'0,
    // 禁止直接調(diào)用 Object.prototypes 的內(nèi)置屬性
    'no-prototype-builtins'0,
    // 禁止可以在有更簡(jiǎn)單的可替代的表達(dá)式時(shí)使用三元操作符
    'no-unneeded-ternary''error',
    // 禁止重復(fù)模塊導(dǎo)入
    'no-duplicate-imports''error',
    // 禁止在對(duì)象中使用不必要的計(jì)算屬性
    'no-useless-computed-key''error',
    // 強(qiáng)制使用一致的縮進(jìn)
    indent: ['error'2],
    // 強(qiáng)制使用駱駝拼寫法命名約定
    camelcase0,
    // 強(qiáng)制類方法使用 this
    'class-methods-use-this'0,
    // 要求構(gòu)造函數(shù)首字母大寫
    'new-cap'0,
    // 強(qiáng)制一致地使用 function 聲明或表達(dá)式
    'func-style'0,
    // 強(qiáng)制一行的最大長(zhǎng)度
    'max-len'0,
    // 要求 return 語句要么總是指定返回的值,要么不指定
    'consistent-return'0,
    // 強(qiáng)制switch要有default分支
    'default-case'2,
    // 強(qiáng)制剩余和擴(kuò)展運(yùn)算符及其表達(dá)式之間有空格
    'rest-spread-spacing''error',
    // 要求使用 const 聲明那些聲明后不再被修改的變量
    'prefer-const''error',
    // 強(qiáng)制箭頭函數(shù)的箭頭前后使用一致的空格
    'arrow-spacing''error',
  },
    overrides: [
    {
      files: ['*.vue'],
      rules: {
        // 要求組件名稱總是多個(gè)單詞
        'vue/multi-word-component-names'0,
      },
    },
  ],
});
復(fù)制代碼

關(guān)于更多配置項(xiàng)信息,請(qǐng)前往 ESLint 官網(wǎng)查看 ESLint-Configuring[32]

創(chuàng)建 ESLint 過濾規(guī)則

在項(xiàng)目根目錄添加一個(gè) .eslintignore 文件,內(nèi)容如下:

dist
node_modules
!.prettierrc.js
復(fù)制代碼

集成 Prettier 配置

Prettier[33] 是一款強(qiáng)大的代碼格式化工具,支持 JavaScript、TypeScript、CSS、SCSS、Less、JSX、Angular、Vue、GraphQL、JSON、Markdown 等語言,基本上前端能用到的文件格式它都可以搞定,是當(dāng)下最流行的代碼格式化工具。

安裝依賴

npm i prettier -D
復(fù)制代碼

安裝插件

Visual Studio Code 編輯器使用 Prettier 配置需要下載插件 Prettier - Code formatter 。

Prettier - Code formatter

JetBrains 系列編輯器(WebStorm、IntelliJ IDEA 等)則不用額外安裝插件,可直接使用 Prettier 配置。

創(chuàng)建 Prettier 配置文件

Prettier 支持多種格式的配置文件[34],比如 .json.yml、.yaml、.js等。

在項(xiàng)目根目錄創(chuàng)建 .prettierrc.js 文件,并填入以下內(nèi)容:

module.exports = {
  // 一行最多 120 字符
  printWidth120,
  // 使用 2 個(gè)空格縮進(jìn)
  tabWidth2,
  // 不使用縮進(jìn)符,而使用空格
  useTabsfalse,
  // 行尾需要有分號(hào)
  semitrue,
  // 使用單引號(hào)
  singleQuotetrue,
  // 對(duì)象的 key 僅在必要時(shí)用引號(hào)
  quoteProps'as-needed',
  // jsx 不使用單引號(hào),而使用雙引號(hào)
  jsxSingleQuotefalse,
  // 末尾需要有逗號(hào)
  trailingComma'all',
  // 大括號(hào)內(nèi)的首尾需要空格
  bracketSpacingtrue,
  // jsx 標(biāo)簽的反尖括號(hào)需要換行
  jsxBracketSameLinefalse,
  // 箭頭函數(shù),只有一個(gè)參數(shù)的時(shí)候,也需要括號(hào)
  arrowParens'always',
  // 每個(gè)文件格式化的范圍是文件的全部?jī)?nèi)容
  rangeStart0,
  rangeEndInfinity,
  // 不需要寫文件開頭的 @prettier
  requirePragmafalse,
  // 不需要自動(dòng)在文件開頭插入 @prettier
  insertPragmafalse,
  // 使用默認(rèn)的折行標(biāo)準(zhǔn)
  proseWrap'preserve',
  // 根據(jù)顯示樣式?jīng)Q定 html 要不要折行
  htmlWhitespaceSensitivity'css',
  // vue 文件中的 script 和 style 內(nèi)不用縮進(jìn)
  vueIndentScriptAndStylefalse,
  // 換行符使用 lf
  endOfLine'lf',
  // 格式化嵌入的內(nèi)容
  embeddedLanguageFormatting'auto',
  // html, vue, jsx 中每個(gè)屬性占一行
  singleAttributePerLinefalse,
};
復(fù)制代碼

關(guān)于更多配置項(xiàng)信息,請(qǐng)前往 Prettier 官網(wǎng)查看 Prettier-Options[35]

創(chuàng)建 Prettier 過濾規(guī)則

在項(xiàng)目根目錄添加一個(gè) .prettierignore 文件,內(nèi)容如下:

## OS
.DS_Store
.idea
.editorconfig
pnpm-lock.yaml
.npmrc

#
 Ignored suffix
*.log
*.md
*.svg
*.png
*.ico
*ignore

#
# Local
.husky

#
# Built-files
.cache
dist
復(fù)制代碼

解決 Prettier 和 ESLint 沖突

本項(xiàng)目中的 ESLint 配置使用了 Airbnb JavaScript 風(fēng)格指南校驗(yàn),其規(guī)則之一是_代碼結(jié)束后面要加分號(hào)_,而在 Prettier 配置文件中加了_代碼結(jié)束后面不加分號(hào)_配置項(xiàng),從而沖突了。

解決兩者沖突問題,需要用到 eslint-plugin-prettiereslint-config-prettier。

  • eslint-plugin-prettier 將 Prettier 的規(guī)則設(shè)置到 ESLint 的規(guī)則中
  • eslint-config-prettier 關(guān)閉 ESLint 中與 Prettier 中會(huì)發(fā)生沖突的規(guī)則

最后形成優(yōu)先級(jí):Prettier 配置規(guī)則 > ESLint 配置規(guī)則

安裝依賴

npm i eslint-plugin-prettier eslint-config-prettier -D
復(fù)制代碼

修改 ESLint 配置文件

修改 .eslintrc.js 文件,在 extends 中添加 plugin:prettier/recommended 規(guī)則(此規(guī)則一定要加在最后)。

module.exports = {
  extends: [
    'airbnb-base',
    'eslint:recommended',
    'plugin:vue/vue3-essential',
    'plugin:vue/vue3-recommended',
    'plugin:prettier/recommended'
  ],
}
復(fù)制代碼

自動(dòng)格式化代碼

Visual Studio Code 在 settings.json 設(shè)置文件中,增加以下代碼:

{
  "editor.codeActionsOnSave": {
    "source.fixAll"true,
    "source.fixAll.eslint"true
  }
}
復(fù)制代碼

WebStorm 打開設(shè)置窗口,按如下操作,最后點(diǎn)擊 Apply -> OK

WebStorm

集成 Stylelint 配置

Stylelint 是一個(gè)強(qiáng)大、先進(jìn)的 CSS 代碼檢查器(linter),可以幫助你規(guī)避 CSS 代碼中的錯(cuò)誤并保持一致的編碼風(fēng)格。

安裝依賴

  • `Stylelint`[36] - Stylelint 本體
  • `stylelint-config-prettier`[37] - 關(guān)閉 Stylelint 中與 Prettier 中會(huì)發(fā)生沖突的規(guī)則
  • `stylelint-config-standard`[38] - Stylelint 官方推薦規(guī)則
  • `stylelint-config-recommended-vue`[39] - 檢驗(yàn) vue 文件中的樣式
  • `stylelint-order`[40] - CSS 屬性順序規(guī)則插件
npm i stylelint stylelint-config-prettier stylelint-config-standard stylelint-config-recommended-vue stylelint-order -D
復(fù)制代碼

安裝插件

Visual Studio Code 編輯器使用 Stylelint 配置需要下載插件 Stylelint 。

Stylelint

JetBrains 系列編輯器(WebStorm、IntelliJ IDEA 等)則不用額外安裝插件。

創(chuàng)建 Stylelint 配置文件

在項(xiàng)目根目錄創(chuàng)建 .stylelintrc.js 文件,并填入以下內(nèi)容:

module.exports = {
  roottrue,
  defaultSeverity'error',
  extends: [
    'stylelint-config-standard',
    'stylelint-config-prettier'
  ],
  plugins: ['stylelint-order'],
  rules: {
    // 不允許未知函數(shù)
    'function-no-unknown'null,
    // 指定類選擇器的模式
    'selector-class-pattern'null,
    // 禁止空源碼
    'no-empty-source'null,
    // 指定字符串使用單引號(hào)
    'string-quotes''single',
    // 禁止未知的@規(guī)則
    'at-rule-no-unknown': [
      true,
      {
        ignoreAtRules: [
          'tailwind',
          'apply',
          'variants',
          'responsive',
          'screen',
          'function',
          'if',
          'each',
          'include',
          'mixin',
        ],
      },
    ],
    // 指定@規(guī)則名的大小寫
    'at-rule-name-case''lower',
    // 指定縮進(jìn)
    indentation: [
      2,
      {
        severity'warning',
      },
    ],
    // 禁止未知的偽類選擇器
    'selector-pseudo-class-no-unknown': [
      true,
      {
        ignorePseudoClasses: ['global'],
      },
    ],
    // 禁止未知的偽元素選擇器
    'selector-pseudo-element-no-unknown': [
      true,
      {
        ignorePseudoElements: ['v-deep'],
      },
    ],
    'order/properties-order': [
      'position',
      'top',
      'right',
      'bottom',
      'left',
      'z-index',
      'display',
      'justify-content',
      'align-items',
      'float',
      'clear',
      'overflow',
      'overflow-x',
      'overflow-y',
      'margin',
      'margin-top',
      'margin-right',
      'margin-bottom',
      'margin-left',
      'padding',
      'padding-top',
      'padding-right',
      'padding-bottom',
      'padding-left',
      'width',
      'min-width',
      'max-width',
      'height',
      'min-height',
      'max-height',
      'font-size',
      'font-family',
      'font-weight',
      'border',
      'border-style',
      'border-width',
      'border-color',
      'border-top',
      'border-top-style',
      'border-top-width',
      'border-top-color',
      'border-right',
      'border-right-style',
      'border-right-width',
      'border-right-color',
      'border-bottom',
      'border-bottom-style',
      'border-bottom-width',
      'border-bottom-color',
      'border-left',
      'border-left-style',
      'border-left-width',
      'border-left-color',
      'border-radius',
      'text-align',
      'text-justify',
      'text-indent',
      'text-overflow',
      'text-decoration',
      'white-space',
      'color',
      'background',
      'background-position',
      'background-repeat',
      'background-size',
      'background-color',
      'background-clip',
      'opacity',
      'filter',
      'list-style',
      'outline',
      'visibility',
      'box-shadow',
      'text-shadow',
      'resize',
      'transition',
    ],
  },
}
復(fù)制代碼

創(chuàng)建 Stylelint 過濾規(guī)則

在項(xiàng)目根目錄添加一個(gè) .stylelintignore 文件,內(nèi)容如下:

# .stylelintignore
# 舊的不需打包的樣式庫(kù)
*.min.css

# 其他類型文件
*.js
*.jpg
*.woff

# 測(cè)試和打包目錄
/test/
/dist/*
/public/*
public/*
/node_modules/
復(fù)制代碼

啟用 Vue 文件支持

Stylelint v14 版本默認(rèn)不支持 vue 文件中的 style 代碼自動(dòng)檢測(cè),詳情查看官方遷移指南[41]

安裝依賴

  • `stylelint-config-html`[42] - 解析 vue 文件
  • `postcss-html`[43] - 使用 stylelint-config-html 依賴的模塊
  • `postcss-less`[44] - 對(duì) less 文件進(jìn)行解析
npm i stylelint-config-html postcss-html postcss-less -D
復(fù)制代碼

修改 Stylelint 配置文件

修改 .stylelintrc.js 文件,添加如下配置:

module.exports = {
  overrides: [
    {
      files: ['*.vue''**/*.vue''*.html''**/*.html'],
      extends: ['stylelint-config-html'],
      rules: {
        // 指定關(guān)鍵幀名稱的模式
        'keyframes-name-pattern'null,
        // 禁止未知的偽類選擇器
        'selector-pseudo-class-no-unknown': [
          true,
          {
            ignorePseudoClasses: ['deep''global'],
          },
        ],
        // 禁止未知的偽元素選擇器
        'selector-pseudo-element-no-unknown': [
          true,
          {
            ignorePseudoElements: ['v-deep''v-global''v-slotted'],
          },
        ],
      },
    },
    {
      files: ['*.less''**/*.less'],
      customSyntax'postcss-less',
      extends: ['stylelint-config-standard''stylelint-config-recommended-vue'],
    },
  ],
};
復(fù)制代碼

修改 Visual Studio Code 工作區(qū)配置

Visual Studio Code 在 settings.json 設(shè)置文件中,增加以下代碼:

{
  "stylelint.validate": ["css""less""postcss""scss""vue""sass"]
}
復(fù)制代碼

集成 husky 和 lint-staged

在項(xiàng)目中已集成 ESLint 和 Prettier,在編碼時(shí),這些工具可以對(duì)代碼進(jìn)行實(shí)時(shí)校驗(yàn),在一定程度上能有效規(guī)范所寫代碼,但有些人可能覺得這些限制很麻煩,從而選擇視“提示”而不見,依舊按自己編程風(fēng)格來寫代碼,或者干脆禁用掉這些工具,開發(fā)完成就直接把代碼提交到了倉(cāng)庫(kù),日積月累,ESLint 也就形同虛設(shè)。

所以,還需要做一些限制,讓沒通過 ESLint 檢測(cè)和修復(fù)的代碼禁止提交,從而保證倉(cāng)庫(kù)代碼都是符合規(guī)范的。

為了解決這個(gè)問題,需要用到 Git Hook,在本地執(zhí)行 git commit 的時(shí)候,就對(duì)所提交的代碼進(jìn)行 ESLint 檢測(cè)和修復(fù)(即執(zhí)行 eslint \--fix),如果這些代碼沒通過 ESLint 規(guī)則校驗(yàn),則禁止提交。

實(shí)現(xiàn)這一功能,需要借助 husky[45] + lint-staged[46] 。

配置 husky

注意:本項(xiàng)目使用 husky 6.x 版本,6.x 版本配置方式跟之前版本有較大差異,當(dāng)發(fā)現(xiàn)配置方法不一致時(shí),一切以 husky 官網(wǎng)[47]為準(zhǔn)。

使用 husky-init 命令快速在項(xiàng)目初始化 husky 配置:

# 初始化倉(cāng)庫(kù)
git init

# 初始化
npx husky-init

# 安裝依賴
npm install
復(fù)制代碼

husky 包含很多 hook(鉤子),常用有:pre-commit、commit-msg

使用 pre-commit 來觸發(fā) ESLint 命令,修改 .husky/pre-commit 文件觸發(fā)命令:

eslint --fix ./src --ext .vue,.js,.ts
復(fù)制代碼

pre-commit hook 文件作用是:當(dāng)執(zhí)行 git commit \-m "xxx" 時(shí),會(huì)先對(duì) src 目錄下所有的 .vue、.js、.ts 文件執(zhí)行 eslint \--fix 命令,如果 ESLint 通過,成功 commit,否則終止 commit。

但是又存在一個(gè)問題:有時(shí)候明明只改動(dòng)了一兩個(gè)文件,卻要對(duì)所有的文件執(zhí)行 eslint \--fix。

假如這是一個(gè)歷史項(xiàng)目,在中途配置了 ESLint 規(guī)則,那么在提交代碼時(shí),也會(huì)對(duì)其他未修改的“歷史”文件都進(jìn)行檢查,可能會(huì)造成大量文件出現(xiàn) ESLint 錯(cuò)誤,顯然這不是我們想要的結(jié)果。

所以只需要用 ESLint 修復(fù)此次寫的代碼,而不去影響其他的代碼,此時(shí)需要借助 lint-staged 工具。

配置 lint-staged

lint-staged 一般結(jié)合 husky 來使用,它可以讓 husky 的 hook 觸發(fā)的命令只作用于 git 暫存區(qū)的文件,而不會(huì)影響到其他文件。

安裝依賴

npm i lint-staged -D
復(fù)制代碼

新增配置

package.json 里增加 lint-staged 配置項(xiàng):

{
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "prettier --write",
      "eslint --fix"
    ],
    "*.vue": [
      "prettier --write",
      "eslint --fix",
      "stylelint --fix"
    ],
    "*.{html,vue,vss,sass,less}": [
      "prettier --write",
      "stylelint --fix"
    ],
    "package.json": [
      "prettier --write"
    ],
    "*.md": [
      "prettier --write"
    ]
  },
}
復(fù)制代碼

修改觸發(fā)命令

修改 .husky/pre-commit 文件觸發(fā)命令為:

npx lint-staged
復(fù)制代碼
pre-commit

經(jīng)過以上配置之后,就可以在每次提交之前對(duì)所有代碼進(jìn)行格式化,保證線上代碼的規(guī)范性。

提交規(guī)范

多人協(xié)作項(xiàng)目中,在提交代碼環(huán)節(jié),也存在一種情況:不能保證每個(gè)人對(duì)提交信息的準(zhǔn)確描述,因此會(huì)出現(xiàn)提交信息紊亂、風(fēng)格不一致的情況。

如果 git commit 的描述信息精準(zhǔn),在后期維護(hù)和 Bug 處理時(shí)會(huì)變得有據(jù)可查,項(xiàng)目開發(fā)周期內(nèi)還可以根據(jù)規(guī)范的提交信息快速生成開發(fā)日志,從而方便我們追蹤項(xiàng)目和把控進(jìn)度。

社區(qū)最流行、最知名、最受認(rèn)可的 Angular[48] 團(tuán)隊(duì)提交規(guī)范:

Angular 團(tuán)隊(duì)提交規(guī)范

參考鏈接: Angular Style Commit Message Conventions[49]

Commit Message 格式規(guī)范

commit message 由 Header、Body、Footer 組成。

<Header>

<Body>

<Footer>
復(fù)制代碼

Header

Header 部分包括三個(gè)字段 type(必需)、scope(可選)和 subject(必需)。

<type>(<scope>): <subject>
復(fù)制代碼

type

type 用于說明 commit 的提交類型(必須是以下幾種之一)。

描述
feat新增功能
fix修復(fù)問題
docs文檔變更
style代碼格式(不影響功能,例如空格、分號(hào)等格式修正)
refactor代碼重構(gòu)
perf改善性能
test測(cè)試
build變更項(xiàng)目構(gòu)建或外部依賴(例如 scopes: webpack、gulp、npm 等)
ci更改持續(xù)集成軟件的配置文件和 package 中的 scripts 命令,例如 scopes: Travis, Circle 等
chore變更構(gòu)建流程或輔助工具
revert代碼回退

scope

scope 用于指定本次 commit 影響的范圍。

scope 依據(jù)項(xiàng)目而定,例如在業(yè)務(wù)項(xiàng)目中可以依據(jù)菜單或者功能模塊劃分,如果是組件庫(kù)開發(fā),則可以依據(jù)組件劃分。

subject

subject 是本次 commit 的簡(jiǎn)潔描述,長(zhǎng)度約定在 50 個(gè)字符以內(nèi),通常遵循以下幾個(gè)規(guī)范:

  • 用動(dòng)詞開頭,第一人稱現(xiàn)在時(shí)表述,例如:change 代替 changed 或 changes
  • 第一個(gè)字母小寫
  • 結(jié)尾不加句號(hào)(.)

Body

body 是對(duì)本次 commit 的詳細(xì)描述,可以分成多行。

跟 subject 類似,用動(dòng)詞開頭,body 應(yīng)該說明修改的原因和更改前后的行為對(duì)比。

Footer

如果本次提交的代碼是突破性的變更或關(guān)閉缺陷,則 Footer 必需,否則可以省略。

  • 突破性的變更

    當(dāng)前代碼與上一個(gè)版本有突破性改變,則 Footer 以 BREAKING CHANGE 開頭,后面是對(duì)變動(dòng)的描述、以及變動(dòng)的理由。

  • 關(guān)閉缺陷

    如果當(dāng)前提交是針對(duì)特定的 issue,那么可以在 Footer 部分填寫需要關(guān)閉的單個(gè) issue 或一系列 issues。

參考例子

  • feat

    feat(browser): onUrlChange event (popstate/hashchange/polling)

    Added new event to browser:
    - forward popstate event if available
    - forward hashchange event if popstate not available
    - do polling when neither popstate nor hashchange available

    Breaks $browser.onHashChange, which was removed (use onUrlChange instead)
    復(fù)制代碼
  • fix

    fix(compile): couple of unit tests for IE9

    Older IEs serialize html uppercased, but IE9 does not...
    Would be better to expect case insensitive, unfortunately jasmine does
    not allow to user regexps for throw expectations.

    Closes #392
    Breaks foo.bar api, foo.baz should be used instead
    復(fù)制代碼
  • style

    style(location): add couple of missing semi colons
    復(fù)制代碼
  • chore

    chore(release): v3.4.2
    復(fù)制代碼

集成 cz-git 實(shí)現(xiàn)規(guī)范提交

一款工程性更強(qiáng),輕量級(jí),高度自定義,標(biāo)準(zhǔn)輸出格式的 commitizen[50] 適配器

官方網(wǎng)站:cz-git[51]

cz-git

安裝依賴

npm install -D cz-git
復(fù)制代碼

指定適配器

修改 package.json 文件,添加 config 指定使用的適配器

{
  "scripts": {},
  "config": {
    "commitizen": {
      "path""node_modules/cz-git"
    }
  }
}
復(fù)制代碼

自定義配置(可選)

cz-git 與 commitlint[52] 進(jìn)行聯(lián)動(dòng)給予校驗(yàn)信息,所以可以編寫于 commitlint[53] 配置文件之中。

例如:(? 配置模板[54])

/** @type {import('cz-git').UserConfig} */
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {},
  prompt: {
    useEmojifalse,
    emojiAlign'center',
    allowCustomIssuePrefixsfalse,
    allowEmptyIssuePrefixsfalse,
  },
};
復(fù)制代碼

本項(xiàng)目配置文件可參考:commitlint.config.js[55]

全局使用

全局安裝的好處在于:在任何項(xiàng)目下都可以利用 czgit cz 命令啟動(dòng)命令行工具,生成標(biāo)準(zhǔn)化 commit message

安裝全局依賴

npm install -g cz-git commitizen
復(fù)制代碼

全局配置適配器類型

echo '{ "path": "cz-git" }' > ~/.czrc
復(fù)制代碼

自定義配置(可選)

方式一: 編輯 ~/.czrc 文件以 json 形式添加配置,例如:

{
  "path""cz-git",
  "useEmoji"true
}
復(fù)制代碼

方式二:與 commitlint[56] 配合,在 $HOME 路徑下創(chuàng)建配置文件 (↓ 配置模板[57])

集成 commitlint 驗(yàn)證規(guī)范提交

在“代碼規(guī)范”章節(jié)中提到,盡管制定了規(guī)范,但在多人協(xié)作的項(xiàng)目中,總有些人依舊我行我素。

因此提交代碼這個(gè)環(huán)節(jié),也增加一個(gè)限制:只讓符合 Angular 規(guī)范的 commit message 通過。

此功能需借助 @commitlint/config-conventional@commitlint/cli 工具來實(shí)現(xiàn)。

安裝

  • `@commitlint/cli`[58] - Commitlint 本體
  • `@commitlint/config-conventional`[59] - 通用提交規(guī)范
npm i @commitlint/cli @commitlint/config-conventional -D
復(fù)制代碼

配置

在項(xiàng)目根目錄創(chuàng)建 commitlint.config.js 文件,并填入以下內(nèi)容:

module.exports = {
  extends: ['@commitlint/config-conventional']
}
復(fù)制代碼

使用 husky 命令在 .husky 目錄下創(chuàng)建 commit-msg 文件,并在此執(zhí)行驗(yàn)證命令:

npx husky add .husky/commit-msg "npx --no-install commitlint --edit $1"
復(fù)制代碼
commit-msg

本項(xiàng)目完整代碼托管在 GitHub 倉(cāng)庫(kù)[60],歡迎點(diǎn)亮小星星 ????

自動(dòng)部署

本章節(jié)將介紹如何使用 CI(Continuous Integration 持續(xù)集成)服務(wù)來完成項(xiàng)目部署工作。

常見的 CI 工具有 GitHub Actions、GitLab CI、Travis CI、Circle CI 等。

本項(xiàng)目使用 GitHub Actions 來完成這一操作。

?? 參考鏈接:GitHub Actions 入門教程[61]

創(chuàng)建 GitHub 倉(cāng)庫(kù)

因?yàn)?GitHub Actions 只對(duì) GitHub 倉(cāng)庫(kù)有效,所以創(chuàng)建 GitHub 倉(cāng)庫(kù)[62]來托管項(xiàng)目代碼。

創(chuàng)建 GitHub 倉(cāng)庫(kù)
  • master 分支存儲(chǔ)項(xiàng)目源代碼
  • gh-pages 分支存儲(chǔ)打包后的靜態(tài)文件

創(chuàng)建 GitHub Token

創(chuàng)建一個(gè)有 repoworkflow 權(quán)限的 GitHub Token[63]

創(chuàng)建 GitHub Token

注意:新生成的 Token 只會(huì)顯示一次。

Token

添加 Actions secret

將上述創(chuàng)建的 Token 添加到 GitHub 倉(cāng)庫(kù)中的 Secrets 里,并將這個(gè)新增的 secret 命名為 VITE_VUE_DEPLOY

步驟:倉(cāng)庫(kù) -> Settings -> Secrets -> Actions -> New repository secret。

New secret

注意:新創(chuàng)建的 secret VITE_VUE_DEPLOY 在 Actions 配置文件中要用到,兩個(gè)地方需保持一致!

修改 package.json

打開 package.json 文件,新增 homepage 字段,表示該應(yīng)用發(fā)布后的根目錄(參見官方文檔[64])。

"homepage""https://[username].github.io/github-actions-demo",
復(fù)制代碼

上面代碼中,將 [username] 替換成你的 GitHub 用戶名,參見范例[65]。

創(chuàng)建 Actions 配置文件

(1)在項(xiàng)目根目錄下創(chuàng)建 .github 目錄。

(2)在 .github 目錄下創(chuàng)建 workflows 目錄。

(3)在 workflows 目錄下創(chuàng)建 deploy.yml 文件。

.github/workflows/deploy.yml
name: Vite Vue Deploy

on:
  push:
    # master 分支有 push 時(shí)觸發(fā)
    branches: [master]

jobs:
  deploy:
    # 指定虛擬機(jī)環(huán)境
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [14.x, 16.x]

    steps:
      - name: Checkout
        # 拉取 GitHub 倉(cāng)庫(kù)代碼
        uses: actions/checkout@v3

      - name: Use Node.js ${{ matrix.node-version }}
        # 設(shè)定 Node.js 環(huán)境
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}

      - name: Install
        # 安裝依賴
        run: npm install

      - name: Build
        # 打包
        run: npm run build

      - name: Deploy
        uses: JamesIves/github-pages-deploy-action@v4
        with:
          # 部署打包目錄
          folder: dist
          # 密鑰名
          token: ${{ secrets.VITE_VUE_DEPLOY }}
          # 分支
          branch: gh-pages
復(fù)制代碼

?? 通過此鏈接 ElanYoung.github.io/vite-vue-js…[66] 即可訪問本項(xiàng)目

文章總結(jié)

本文從技術(shù)選項(xiàng)到架構(gòu)搭建、從代碼規(guī)范約束到提交信息規(guī)范約束,一步一步帶領(lǐng)大家如何從一個(gè)最簡(jiǎn)單的前端項(xiàng)目骨架到規(guī)范的前端工程化環(huán)境,基本涵蓋前端項(xiàng)目開發(fā)的整個(gè)流程,特別適合剛接觸前端工程化的同學(xué)學(xué)習(xí)。

因篇幅較長(zhǎng),所涉及技術(shù)點(diǎn)較多,難免會(huì)出現(xiàn)錯(cuò)誤,希望大家多多指正,謝謝大家!

關(guān)于本文

作者:威廉王子

https://juejin.cn/post/7156957907890733063

最后


歡迎關(guān)注【前端瓶子君】??ヽ(°▽°)ノ?
回復(fù)「算法」,加入前端編程源碼算法群,每日一道面試題(工作日),第二天瓶子君都會(huì)很認(rèn)真的解答喲!
回復(fù)「交流」,吹吹水、聊聊技術(shù)、吐吐槽!
回復(fù)「閱讀」,每日刷刷高質(zhì)量好文!
如果這篇文章對(duì)你有幫助,在看」是最大的支持
 》》面試官也在看的算法資料《《
“在看和轉(zhuǎn)發(fā)”就是最大的支持


瀏覽 140
點(diǎn)贊
評(píng)論
收藏
分享

手機(jī)掃一掃分享

分享
舉報(bào)
評(píng)論
圖片
表情
推薦
點(diǎn)贊
評(píng)論
收藏
分享

手機(jī)掃一掃分享

分享
舉報(bào)

感谢您访问我们的网站,您可能还对以下资源感兴趣:

国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频 大地99中文在线观看| 国产精品色在线回看| 中文黄片| 91成人视频在线播放| 97欧美| 亚洲天堂无码高清| 天天干天天日天天射| 日韩成人av在线| 波多野结衣与黑人| 成人性爱视频免费在线观看| 中文字幕精品久久久久人妻红杏Ⅰ | 波多野结衣视频无码| 99精品一区二区三区| 北条麻妃一区二区三区在线| 亚洲综合社区在线| 91就要爱爱视频| 人妻在线观看| 99久久久久| 精品欧美| 亚洲热在线观看| 成人无码动漫A片| 91久久| 91视频人人| 高清无码黄| 人人插人人射| 久久精品视频9| 五月婷婷黄色| 五月天干美女| 丰满人妻一区二区三区| 77777免费观看电视剧推荐爱的教育| 国产黄片免费视频| 欧洲美一区二区三区亚洲| 中文字幕电影| 国产人体视频| 日韩欧美一区二区在线观看| 中国丰满妇BBwBBwHD| 国产精品国产三级国产| 久久另类TS人妖一区二区免费| 久久久久久久极品内射| 污污污污污www在线观看优势 | 少妇精品久久久久久久久久| 久久久久999| 俺也来最新色视频| 成人在线第一页| 国产精品在线免费观看| 日韩在线观看网站| 精品成人| 日逼黄片| 一级无码毛片| 天天干天天舔| Japanese在线观看| 麻豆亚洲AV成人无码久久精品| 国产无遮挡又黄又爽| JULIA超乳JULIA无码| 亚洲天堂日本| 丰满岳乱妇一区二区三区全文阅读| 国产在线视频91| 99热在线只有精品| 亚州高清无码视频| 天天干天天舔| 日本黄色一级| 免费亚洲无码| 强辱丰满人妻HD中文字幕| 久久嫩草精品久久久久| 中文字幕一区二区三区四虎在线 | 欧美国产激情| 成人片网址| 欧美艹逼| 国产三级毛片| 国产情趣网站| 加勒比在线视频| 国产二区视频| 五月天堂婷婷| 丁香视频在线观看| 人人摸人人看人人| 亚洲精品成人AV| 1000部毛片A片免费视频| 国内精品久久久久久久久久变脸| 中文字幕在线免费观看电影| 大骚逼影院| 亚洲人操逼视频| jizz麻豆| 无码成人A片在线观看| 在线看黄网站| 成年人网站在线免费观看| 日韩免费一级片| 无码三级片在线观看| AV中文在线| 成人精品视频| 青青草成人免费在线视频| 少妇人妻一区| 91视频网站入口| 午夜亚洲AⅤ无码高潮片苍井空| 丝袜久久| 婷婷操逼网| 国产精品二| 久久伊人草| 99热99精品| 久在线| 国产一级在线| 在线观看免费完整版中文字幕视频| 色婷婷久久久久swag精品| 中文乱码在线观看| 久久久久久久久久久国产精品| 亚洲中字幕| 国内精品久久久久久久久98| 不卡无码高清| 国产精品久久毛片A片| 亚洲无码一级| 婷婷色在线| 99re在线| 久久黄色视频免费看| 一级特黄大片录像i| 欧美AA级毛片| 国产熟女一区| 亚洲高清成人动漫| 亚洲精品一区二区三| 日本精品一区二区三区四区的功能| 91精品国产综合久久久蜜臀酒店 | 色婷婷一区二区三区四区五区精品视| 麻豆传媒av| 大香蕉久在线| 国产成人无码一区二区在线观看| 日韩免费视频观看| 国产灬性灬淫灬欲水灬| 精品无码一区二区三| 黑巨茎大战欧美白妞| 婷婷丁香人妻天天爽| 色丁香在线| 五月婷在线视频| 强开小嫩苞一区二区三区网站| 在线中文字幕AV| 五月婷在线| 麻豆AV在线| 国产欧美综合在线三区| 日韩成人在线看| 亚洲欧美在线视频免费| 久久黄色视频免费观看| www.色悠悠| www.天天日| 亚洲女与黑人正在播放| 欧美黄片无码| 激情视频综合网| 熟女综合网| 黄色一级a片| 国产精品高潮无套内谢| 亚洲成人精品视频| 亚洲激情AV| 黄色福利| 黄色免费在线观看视频| av黄色| 天天操天天谢| 国产成人秘在线观看免费网站 | 色999| 亚洲日韩AV在线| 亚洲在线高清视频| 日韩一级在线| 精产国品一区二区| 婷婷五月综合激情| 欧美日韩性爱网站| 日韩精品电影| 欧美偷拍精品| 操逼视频免费在线观看| 日韩a| 天堂网av在线| 午夜男人天堂| 日韩,变态,另类,中文,人妻| 日韩在线播放视频| 人妻av在线| 日本成人性爱视频网站一区| 色婷婷欧美在线播放内射| 黄色电影网页| 久久久久无码| 亚洲日韩AV无码专区影院| 一本久久A精品一合区久久久| 偷拍一区二区三区| 一起草在线视频| 高清无码视频在线免费观看| 黄色操逼网站| 午夜成人福利视频| 亚洲精品成人网站| 性爱av天堂| 骚逼AV| 视色视频在线观看| 中文字幕第27页| 午夜理伦| 日本一级黃色大片看免费| 男女操逼视频网站免费观看| 少妇高潮一区二区三区99| 国产亚洲欧洲| 91在线导航| www.a日逼| 亚洲日本无码50p| 初尝人妻滑进去了莹莹视频 | 天堂在线最新资源| 在线h网站| 国产精品乱子伦一区二区三区视频 | 1024在线视频| 国内自拍青青| 狠狠干综合网| 欧美亚洲性爱| 激情性爱婷婷色五月| 毛片大香蕉| 中文有码在线观看| 日韩天天干| 久草大香蕉在线视频| 青青色视频| 无码免费视频在线观看| 欧美丝袜脚交xxxxBH| 曰曰干| av色色| 青青草国产| 手机无码在线播放| 人人操超碰在线| 伊人网视频在线| 欧美性BBwBBwBBwHD| 欧美日屄视频| 国产日韩视频| 日皮做爱视频网站| 丰满人妻-区二区三区| 特級西西444WWw高清大膽| 91免费在线| 在线观看中文字幕亚洲| 思思在线视频| 久久免费黄色视频| 国产一级AA片| JULIA超乳JULIA无码| 丁香六月激情| 国产欧美精品一区二区三区 | 草视频| 欧美精品18videosex性欧美 | 日韩无码破解| 国产精品一区二区三区在线| 亚洲无码AV电影| 五月天婷婷在线播放视频免费观看| 亚洲色热| 久久视频免费看| 亚洲日韩中文字幕无码| 人妻制服丝袜| 婷婷五月天社区| 欧美一级做| 一道本无码在线播放| 女人的天堂AV| 夜夜操天天| 97在线视频免费观看| 黄色无码视频| 婷婷中文字幕亚洲| 图片区小说区区亚洲五月| 青青草无码在线视频| 国产91无码| 黄页网站免费观看| 中文无码日本高潮喷水| 熟女人妻ThePorn| 国产第一页在线观看| 99视频免费在线| 久久在线免费视频| 国产色片| 国产成人777777精品综合| 亚洲精品成人无码毛片| 中文字幕视频在线免费观看| 久久久青草| 91人妻无码一区二区三区| 无码精品一区二区免费| 日韩中文字幕高清| 高颜值呻吟给力| gogogo日本免费观看高清电视剧的注意 | 91网站在线免费观看| 日韩精品一区二区三区中文在线| 亚洲中文字幕在线视频| 俺也来最新色视频| 操B无码| 污污污污污www在线观看优势 | 五月激情视频| 欧一美一婬一伦一区?| 成人婷婷| 久久99精品久久久久婷婷| 韩国三级HD久久精品| 成人黄片网站| av无码av天天av天天爽| www.男人天堂| 玖玖91| 欧美一级黄色大片| gogogo高清在线观看免费直播中国| 成人免费黄色视频网站| 成人性生活免费视频| 亚洲国产成人精品女人久久| 人妻成人网| 日韩图片区小说视频区日| 成人一级a片| 超碰超碰| 在线视频福利导航| www.天天日| 日韩无码福利| 色999| 91视频在线免费观看app| 欧美在线免费观看| 久久er视频| 国产成人无码AⅤ片免费播放 | 日本乱轮视频| 激情五月天网| 大鸡巴操小逼视频| 久久久久无码| 一区二区成人免费视频| 日韩无码精品电影| 人妻超碰在线| 国产超碰| 色吧综合网| 思思热思思操| 色玉米地熟妇| 黄色激情五月| 俺去也| 黄色电影天堂网| 北条麻妃无码在线观看| 国产精品精品精品| 成人自拍偷拍视频| xxxxxbbbbb| 欧美视频免费| 黄色免费无码| 欧美一区二区在线观看| 91爱爱·com| 91视频精品| 蜜桃视频日韩| 亚洲电影在线观看| 人人干视频| 日韩免费视频在线观看| 99久久国产热无码精品免费| 国产XXXX| 亚洲男人的天堂av| 免费看成人747474九号视频在线观看| 欧美熟妇一区二区| 日韩a| 91超碰免费在线| 无码网址| 无码精品一区二区在线| 青青草资源站| 丁香五月六月婷婷| 美女视频黄a视频全免费不卡| 超碰9999| av福利在线观看| 成人啪啪网站| 久久婷婷精品| 日韩va中文字幕无码免费| 91久久精品一区二区三区| 91在线无码精品秘| 日韩成人无码精品| 天堂在线| 成人黄网站免费视频| 日韩在线小视频| 少妇高潮在线| 91熟女丰满原味| 俺来也俺去也www色官网| 人人爱人人妻人人操| 国产伦精一品二品三品app| 蜜臀久久99精品久久一区二区| 黄色成人网站在线播放| 99视频免费在线| 亚洲日韩中文无码| 美女性爱视频网站| 欧美大香蕉视频| 91在线不卡| 色高清无码免费视频| 亚洲秘一区二区三区-精品亚洲二区-| 91丨人妻丨国产| 波多野成人无码精品视频| 人妖无码| 国产成人无码一区二区在线| 成人免费黄色网| 久草视频在线播放| 99Re66精品免费视频| 国产精品视频一区二区三区在线观看 | 水蜜桃视频在线观看| 91丨PORNY丨对白| 成人视频91| 丰满岳乱妇一区二区三区| 国产3p露脸普通话对白| 夜夜操免费视频| 亚洲AV无码久久寂寞少妇多毛| av在线资源| 做爰视频毛片下载蜜桃视频。| 九九色在线视频| 日韩精品一二三区| 老汉av| 午夜黄色视频| 亚洲AV无码国产综合专区| 欧美男人天堂网| 欧美一级片在线| 欧美熟妇高潮流白浆| 国产视频久久| 老婆中文字幕乱码中文乱码| 美日韩一级| 69黄色视频| 性久久久久久久久久| 黄色视频在线观看亚洲一区二区三区免费 | 91爱爱网| 国产小黄片在线| 中国熟女网站| 中文字幕无码视频在线观看| 日韩69视频| 日韩国产一区| 美女天天干| 苍井空中文字幕在线观看| 伊人五月天激情| 麻豆AV免费看| 少妇三区| 中文无码日本一级A片久久影视| 一本之道高清数码大全| 91超碰在线观看| 一级黄色录像带| 粉嫩99国产精品久久久久久人妻| 日本不卡一区二区| 内射视频在线免费观看| 日韩免费在线观看| 蜜臀久久99精品久久久电影| 久热精品在线观看视频| 国产精品午夜在线观看| 三级黄色视频| 99er在线观看视频| 伊人蕉 | 亚洲操逼图| 在线A视频| 欧美日韩V| 色网在线| 日韩欧美V| 亚洲操操操操| 国产精品成人电影| 国产综合网站| 欧美日韩中| 国产亚洲av| 久久久久久穴| 免费一级网站| 在线观看免费完整版中文字幕视频| 久久这里只有精品99| 亚洲AV秘无码不卡在线观看| 翔田千里无码精品| 护士小雪的yin荡高日记H视频 | 欧美成人一区二区三区片| 伊人久久大| 久久黄色视频免费观看| 日本一区二区精品| 91人妻无码| 久久九色| 91一起草高清资源| 无码精品黄色片| 波多野结衣成人在线| 成人免费一区| 久久久精品999| 综合久久网| 杨贵妃一级婬片90分钟| 国产精品国产三级国产AⅤ| 色婷婷视频| 草久影院| 熟女人妻在线| 91精品国产乱码| 内射精品| 大香蕉伊人网站| 五月婷在线视频| 亚洲aV影院| 丰满BBwBBwBBwBBW| 国产无码操逼| 日韩人成| 中国免费看片| 中文字幕亞洲高清手機版第617| 亚洲第一视频在线观看| 午夜成人精品一区二区三区| 夜夜骚精品人妻av一区| 大鸡巴操骚逼视频| 亚洲欧美国产高清vA在线播放| 一区二区精品视频| 丁香激情五月天| 男女啪啪| 无码人妻一区二区三区在线视频不卡 | 欧美l∨视| 国产成人av在线观看| 狠狠躁夜夜躁人人爽视频| 一本道无码在线观看| 一二三区视频| 蜜臀AV网| 免费高清无码在线| 操逼网123| 亚洲无码小电影| 亚洲第一大网站| 欧洲AV片| 99精品六月婷婷综合在线| 激情五月丁香婷婷| 超碰免费观看| 天堂va欧美ⅴa亚洲va一夜| 91成人篇| 日韩电影中文字幕| 日韩精品视频一区二区| 九九自拍视频| 成人网站一区二区| 深爱婷婷网| 青青网站| 久久伊人草| 欧美午夜爱爱| 先锋av资源在线| 91大神免费观看| 国产一级特黄A片| 天堂а√在线中文在线新版| 夜色88V精品国产亚洲| 91一区二区在线播放精品| 人人操超碰| 日韩中文字幕电影| 亚洲无套内射| 91大神shunv| 日韩bbbb| 老司机永久免费91| 国产黄片免费观看| 四虎av在线| а中文在线天堂精品| 高清在线无码视频| 国产日本在线视频| 日本精品人妻无码77777| 丝袜足交视频| 精品孕妇孕交无码专区| 婷婷五月中文| 色优久久| 狠狠躁婷婷天天爽综合| 伊人中文字幕| 黄色一级片视频| 麻豆三级片在线观看| 国产美女福利| 国产夫妻露脸| 亚洲成人中文字幕在线| 伊人网在线播放| 免费高清无码在线观看| 伊人春色av| 欧美成人中文字幕在线| AV老鸭窝| 日韩人妻精品一区二区| 日韩在线| 久久影音先锋| 91蝌蚪在线| 久久久8| 黄色视频在线免费观看网站| 成人免费av| 狼人狠干| 色眯眯久久爱| 91狠狠综合久久| 成人久久视频| av在线资源观看| 12——13女人毛片毛片| 少妇一级婬片内射视频| 欧美性爱动态| 欧美国产综合在线| 国产人人干| 免费草逼视频| 日老女人的逼| 欧洲亚洲免费视频| 欧美日韩无码视频| 青青草原免费在线视频| 欧美日韩免费在线视频| 人人操人人干人人操| 日本免费黄色| 激情欧美| 热热av| 日老女人的逼| 无码V| 亚洲无码性爱视频| 国产精品久久久大香蕉| 激情五月天亚洲| 日本三级片免费| 2026无码视频| 午夜嘿嘿| 黄色成人在线| 亲子乱AⅤ一区二区三区| 福利毛片| 东北老女人操逼视频| 做爰视频毛片下载蜜桃视频| 日韩视频91| 加勒比国产在线| 天天日天天干天天爽| 吴梦梦一区二区三区| 开心四房播播第四婷婷| 国产精品久久久久久久久久久久| 有码在线| 一区二区高清视频| 久久黑人| 拍真实国产伦偷精品| 91狠狠色丁香婷婷综合久久精品| 久草久热| 久久九一| 一本一道久久综合| 另类激情网| 日韩av电影免费在线观看| 影视先锋久久| 激情小视频国产在线播放| 在线一区| 日韩视频一区| 五月丁香色婷婷| 中国老女人操逼| 9I免费看片黄| 麻豆国产精品| 狠狠干干| 亚洲综合色色| 日本一级大毛片a一| 99精品色| 中文无码熟妇人妻| 精品国产重口乱子伦| 四川BBB嫩BBBB爽BBBB| 亚洲黄色一级电影| 免费一级网站| 久久久91人妻无码精品蜜桃ID| 骚BBBB槡BBB槡BBB| 欧美51精品| 超碰在线免费播放| 伊人精品大香蕉| 欧美日韩精品一区二区三区| 99成人在线| 精品久久久久久AV2025| 狼友初视频在线观看| 色婷婷av| 青青操逼| 超碰91在线| 国产色婷婷| 国产黄片视频| 欧美黄色一级| 操逼免费看| 日韩精品三区| 91人妻人人操| 免费播放片色情A片| 亚洲一区二区无码| 日韩国产一区| 久久久在线| a√天堂中文8| 91人妻人人澡人人爽人| 色接久久| 在线视频亚洲| 色天堂在线观看| 日本黄色电影网站| 视频一区在线观看| 国产黄色一级片| 日韩成人网站| 操逼网址| AV成人| 国产一级婬片A片免费妖精视频| 国产操逼免费| 尹人香蕉久久| 阿拉伯三级片| 国产又黄又大又粗| 中文字幕自拍偷拍| 国产一级AA大片毛片| 日本午夜影院| 中文字幕亚洲人妻| 亚洲成人精品一区| 日本一区中文字幕| 五月丁香激情在线| 天天日天天干天天草| 欧美亚洲成人在线观看| 日韩人妻无码电影| AV婷婷五月天| 亚洲天堂精品在线| 国产美女高潮视频| 在线无码电影| 亚洲无码成人在线观看| 国产精品色8| 91无码精品国产| 五月网站| 日韩在线视频一区| 欧美视频一区二区三区四区| 日本黄色电影网站| 國產美女AV操逼網站| 国产午夜91人妻| 欧美大鸡| 无码专区av| 日韩69视频| 国产18水真多18精品| 国产成人中文字幕| 国产毛片久久久久久久| 水蜜桃网站在线观看| 亚洲精品秘一区二区三小| www.狠狠干| 日韩欧美A片| 黄网免费在线观看| 免费无码国产在线53| 中文字幕在线观看第一页| 国产性爱精品影片免费看| 亚洲精品一区二区三区蜜桃| 日韩aaa视频| 天天日天天干天天爽| 成人福利视频在线| 97视频精品| 性九九九九九九| 91原创视频| 国产AV一二三区| 国产主播福利| 江苏妇搡BBBB搡BBBB| www.欧美日韩| 手机看片午夜福利网| 麻豆艾秋MD0056在线| 日韩无码一区二区三| 国产区在线视频| 婷婷综合一区| 日韩偷拍网| 免费看成人A片无码照片88hⅤ | 亚洲秘无码一区二区三区,| 亚洲无码综合| 天天看天天干| 激情另类| 亚洲中文字幕免费观看视频| 91成人A片| 大香蕉免费网| 91无码人妻一区二区| 超级人人操| 日韩午夜福利视频| 黄色三级视频在线观看| 国产成人在线视频| 粉嫩一区二区三区四区| 国产噜噜噜噜噜久久久久久久久| 日批视频免费观看| 国产区在线| 亚洲中文字幕在线看| 撸一撸在线| 亚洲高清视频在线| 国产一级黄片| 黄色一级aa片| 97人人爽人人爽人人爽| 午夜亚洲精品| www.黄色电影| 夜夜嗨av| 丁香五月欧美| 免费看欧美日黄片| 日逼导航| 免费的黄色视频在线观看| 一区二区三区在线观看免费| 这里只有精品在线观看| 欧美中出| 亚洲性爱AV| 午夜精品18码视频国产17c| 欧美成人一级a片| 国产一级二级片| 久久久青草| 91精品国产麻豆国产自产在线| 天天干天天操天天干| 高清无码免费视频| 丁香激情视频| 精品国产一级| 国产精品婷婷久久久| 无码操逼视频| 大香蕉中文在线| 肥臀AV在线| 国产一二| 亚洲日韩免费| 在线观看无码av| 欧美猛交| 日韩无码毛片| 超碰在线网| 99热这里是精品| 亚洲网站在线播放| 成人无码人妻| 激情六月天| 人人操碰人人| 黄色小视频免费| 国产精彩无码视频| 久久久久婷婷| 三级片中文字幕| 日都一级A片| 一级片在线免费看| 爱爱午夜福利| 欧美操逼的| 青青草超碰| 一级做a视频| 伊人影院在线看| 中文字幕丰满熟妇人妻| 大香蕉网伊人| 综合+++夜夜| 巨乳一区二区三区| 五月丁香婷婷啪啪| 日韩午夜av| 99乱伦| 欧美日韩逼| 韩国无码中文| 国产精品黄色片| 国产综合AV| 91免费在线视频| 亚洲五月激情| 996re| 91久久香蕉囯产熟女线看蜜桃| 婷婷五月亚洲| 看欧美黄片| 成人A片网| 精品视频91| 国产伦精品一区二区三区妓女| AV网站入口| 麻豆91蜜桃传媒在线观看| AV小说在线观看| 国产成人精品无码免费| 国产顶级理伦| 91亚洲精华国产精华精华液| 大鷄巴成人A片视频| 人妻少妇一区二区三区| 国产AV资源| 日p视频在线观看| 无码无卡| 白嫩外女BBWBBWBBW| 肏亚洲美女| 成人网站一区二区| 尤物91| 伊人久久大香线蕉av一区| 午夜无码鲁丝片午夜精品| 国产黄色一级电影| 黄片无码免费| 日本不卡一区二区三区| 欧美成人A级片| 自拍三区| 2025av天堂网| 99亚洲精品| 成人黄片网| 3d动漫精品一区二区三区在线观看 | 四虎2025在线51| 欧美AAAAAAAAAA特级| 亚洲va欧洲va国产va不卡| 江苏妇搡BBBB搡BBB| 一区二区人妻| 91人妻人人爽人人澡| 91精品国产综合久久蜜臀使用方法 | 国产麻豆AⅤMDMD0071| 中国AV网| 手机在线观看av| 97综合| 91艹艹| 牛牛精品一区二区| 五月丁香影院| 国产精品毛片一区视频播| 日韩理论在线| 亚洲手机视频| 初学影院WWWBD英语完整版在线观看 | 日韩极品视频| 快播激情小说| 国产乱论视频| 无码人妻一区二区三区免费n鬼沢| 美女网站黄| 人妻丰满精品一区二区| 伊人视频在线| 日逼黄片| 日本亚洲精品秘入口A片| 中文字幕15页| 午夜精品秘一区二区三区| 欧美大鸡巴视频| 免费中文字幕av| 亚洲国产高清视频| 国产污视频| 黄色视频电影| 亚洲免费三级片| AA免费视频| 免费成人黄色| 欧美精品黄片| 91精品国产一区二区三区四区大| 无码精品人妻| 99乱伦| 五月婷婷狠狠爱| 91蜜桃在线观看| 国产成人午夜| 一级免费爱爱视频| 美女福利在线| 国产拍拍拍| 色婷婷香蕉| jizz在线观看| 日韩欧美一区二区三区不卡| 真人一级毛毛片| 久久久久久久久国产| 亚洲天堂天天| 2025中文字幕在线| 久久亚洲国产| 中文无码熟妇一区二区| 久草资源在线观看| 一区二区三区在线观看视频| 成人无码自拍| 日韩免费成人| 国产熟妇码视频黑料| 亚洲毛片亚洲毛片亚洲毛片| 国产午夜成人| 另类老妇性BBBWBBW| 晚上碰视频| 亚洲A视频| 中文字幕免费一区| 99在线看| 狠狠狠狠狠| 午夜激情毛片| 99在线视频精品| 做爱的网站| 国产在线观看91| 成人三级片网站| 霸道总裁雷总各种姿势白浆爱情岛论坛 | 乱子伦国产精品| 操逼操逼操逼操逼操逼操逼| 久青草资源福利视频| 中文字幕乱码亚洲中文在线| 韩国无码一区二区| 人人操天天操| 超碰91免费在线观看| 日韩大屌| 欧美三级在线| 色色视频网| 中文字幕在线亚洲| 91传媒在线免费观看| 亚洲综合影院| 青青草五月天色婷婷丁香| 婷婷五月天啪啪| 日韩一级欧美一级|