vue 實(shí)戰(zhàn)各種小技巧(長期更新)

1. vue-cli 構(gòu)建項(xiàng)目

官網(wǎng)地址

  • 命令行
# 全局安裝 vue-cli
$ npm install --global vue-clif
# 創(chuàng)建一個(gè)基于 webpack 模板的新項(xiàng)目
$ vue init webpack your-project-name
# 安裝依賴,走你
$ npm install
# 進(jìn)入項(xiàng)目
$ cd your-project-name
# 開發(fā)版本打包并運(yùn)行
$ npm run dev
# 線上環(huán)境整個(gè)項(xiàng)目打包  生成 dist 可以直接部署到服務(wù)器上的文件夾
npm run build

2. 項(xiàng)目模板中使用 less 方法

原文地址
vue-cli 構(gòu)建的項(xiàng)目默認(rèn)是不支持 less 的,需要自己添加。

  • 首先安裝 less 和 less-loader ,在項(xiàng)目目錄下運(yùn)行如下命令
# npm安裝
$ npm install less less-loader --save-dev
# 或者使用 yarn
$ yarn add less less-loader --dev
  • 安裝成功后审磁,打開 build/webpack.base.conf.js ,在 module.exports = 的對象的 module.rules 后面添加一段:
module.exports = {
    //  此處省略無數(shù)行,已有的的其他的內(nèi)容
    module: {
        rules: [
          //  此處省略無數(shù)行烦绳,已有的的其他的規(guī)則
          {
            test: /\.less$/,
            loader: "style-loader!css-loader!less-loader",
          }
        ]
    }
}
image.png
  • 最后在代碼中的 style 標(biāo)簽中 加上 lang="less" 屬性即可
<style scoped lang="less">

</style>
image.png
  • 之后在項(xiàng)目中測試是否成功
npm install less less-loader --save-dev
npm run dev
image.png
  • 在瀏覽其中打開相應(yīng)頁面,這個(gè)頁面是 / 根頁面點(diǎn)擊跳轉(zhuǎn)過來的子路由
image.png

image.png

可以看到樣式編譯成功了 哦耶~

3. 在 router 下的路由文件里設(shè)置格式配紫,將頁面上路由中默認(rèn)顯示的 #/ 給去掉

// 去掉路由中自帶的 #/ 這種東西
  mode: 'history',
image.png
  • 需要注意的是使用了 history 之后需要在服務(wù)器部署時(shí)增加一些配置径密,具體方法插件下面官方寫的配置方法

配置方法

4. 引入 jquery

  • 安裝
npm install jquery --save
  • 配置
image.png

image.png
// 先在頂部引入 webpack
const webpack  = require('webpack')

// plugins 中添加
new webpack.ProvidePlugin({
      'window.jQuery': 'jquery', // 為了兼容其他的插件
      jQuery: 'jquery',
      $: 'jquery'
    })
  • 使用
image.png

5. :class 使用表達(dá)式

:class="{'想要改變的類名': 判斷條件}
  • 示例圖片
image.png

6. DOM 事件修飾符

<!-- 阻止單擊事件繼續(xù)傳播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重載頁面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修飾符可以串聯(lián) -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修飾符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件監(jiān)聽器時(shí)使用事件捕獲模式 -->
<!-- 即元素自身觸發(fā)的事件先在此處處理,然后才交由內(nèi)部元素進(jìn)行處理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只當(dāng)在 event.target 是當(dāng)前元素自身時(shí)觸發(fā)處理函數(shù) -->
<!-- 即事件不是從內(nèi)部元素觸發(fā)的 -->
<div v-on:click.self="doThat">...</div>
  • 示例躺孝,如下圖所示享扔,這樣寫的話點(diǎn)擊了 li 內(nèi)部的元素的話不會(huì)影響 li 的 click 的點(diǎn)擊事件
image.png

7. vue 使用 clipboard 實(shí)現(xiàn)復(fù)制功能

原文地址

  • 安裝依賴 clipboard.js
npm install clipboard --save
  • 在需要使用的地方 require 引用
var clipboard = require('clipboard');
  • 在頁面加載后調(diào)用該方法即可
image.png

8. 解決 vue-resource 的跨越問題

我這里是 vue-cli 基于 webpack 的項(xiàng)目(注意:在修改了 proxyTable 之后需要在命令行中 npm run dev 重新運(yùn)行下項(xiàng)目底桂,否則是不會(huì)有效果的呀~)

  • 錯(cuò)誤信息

Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.

  • 解決方法:

先找到對應(yīng)的配置 js 文件中的 proxyTable


image.png

修改相應(yīng)的配置


image.png

再在 main.js 中配置 vue-resource 的格式,不配置的話是無法向后臺(tái)傳遞參數(shù)的
image.png

在 vue 文件中的使用
data 中綁定相應(yīng)的靜態(tài)配置


image.png

methods 增加相應(yīng)的方法
image.png

mouted 在 data 數(shù)據(jù)掛載到實(shí)例對象的時(shí)候 惧眠,請求頁面數(shù)據(jù)籽懦,實(shí)現(xiàn)頁面的正常顯示
image.png

9. vue-router 單頁之間如何在 js 中跳轉(zhuǎn)

原文地址

  • 三種寫法
// 字符串
this.$router.push('/home/first')

// 對象
this.$router.push({ path: '/home/first' })

// 命名的路由
this.$router.push({ name: 'home', params: { userId: wise }})
image.png

10. vuex 實(shí)現(xiàn)組件之間數(shù)據(jù)的傳遞

根據(jù) state 可以實(shí)時(shí)的獲取到數(shù)據(jù)
原文地址

  • 安裝
npm install vuex --save
  • 在 src 文件夾中新建一個(gè) stroe 文件夾,并在目錄下新建一個(gè) index.js 文件(已有的話請忽略)氛魁,index.js 文件編輯如下
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

let store = new Vuex.Store({
    state: {
        formData: {} // 企業(yè)提交數(shù)據(jù)表單對象
    }
});

export default store;

  • 在 src 目錄下的 main.js 文件中引入 vuex 文件暮顺,并在實(shí)例化時(shí)添加配置
import Vue from 'vue';
import App from './App';
import router from './router';
import store from './store'; // 引入 vuex

Vue.config.productionTip = false;
Vue.http.options.emulateJSON = true;

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store, // 需要在添加
  components: { App },
  template: '<App/>'
});

  • 之后就可以直接在需要的組件中直接引用,引用具體示例如下
image.png

image.png

控制臺(tái)成功輸出


image.png

11. .eslintrc.js 文件 rules 增加設(shè)置

我的筆記

12. vue 表單操作

我的筆記

13. 解決使用 vux 組件庫時(shí) 與 rem 設(shè)置沖突帶來的問題

  • 思路

將之前 rem 計(jì)算的數(shù)值 html font-size: "100px"秀存,改到 12px捶码,之后連鎖的將 less 中計(jì)算和引用的的值也改下,之后就可以了或链,盡量做到少量的修改即可

  • 將之前的 js 計(jì)算 rem 數(shù)值腳本修改相應(yīng)的數(shù)值
image.png

改過之后的


image.png
  • 修改 less
image.png

改過之后的


image.png
  • 之后就可以是 rem 和 vux 基本正常了

14. 通過 watch 動(dòng)態(tài)的監(jiān)測路由跳轉(zhuǎn)(跳轉(zhuǎn)時(shí))和 APP.vue 中設(shè)置 created 方法實(shí)時(shí)監(jiān)測 path (刷新時(shí))惫恼,來實(shí)現(xiàn) header 文字的改變

  • header.vue
watch: {
    '$route' (to, from) {
        // 檢測路由改變 header 內(nèi)容
        if (to.name === 'Index') {
            this.$store.state.PageTitle = '預(yù)約領(lǐng)號(hào)';
            this.$store.state.isShowBack = false;
        } else if (to.name === 'PreferentialDescription') {
            this.$store.state.PageTitle = '優(yōu)惠說明';
            this.$store.state.isShowBack = true;
        } else if (to.name === 'RuleIntroduction') {
            this.$store.state.PageTitle = '規(guī)則簡介';
            this.$store.state.isShowBack = true;
        } else if (to.name === 'ReservationSuccess') {
            this.$store.state.PageTitle = '預(yù)約排號(hào)';
            this.$store.state.isShowBack = true;
        }
    }
  }

15. vue-router spa (單頁)需要的 nginx 配置,防止出現(xiàn) 404 的情況

官方文檔

image.png

照著上方的圖將代碼復(fù)制至服務(wù)器的 nginx.config 配置文件即可

  1. ssh 遠(yuǎn)程登錄服務(wù)器
ssh username@ipaddress
enter your password
  1. 查找服務(wù)器中的 nginx 相關(guān)目錄澳盐,我這邊是 nginx 服務(wù)器
whereis nginx
image.png
  • /etc/nginx 這個(gè)是 nginx 相關(guān)配置的目錄
  • /usr/share/nginx 是靜態(tài)文件的目錄
  • 進(jìn)入 html 目錄祈纯,這個(gè)就是默認(rèn)的存放項(xiàng)目文件的目錄
image.png
  • 修改 nginx 默認(rèn)的配置文件
# 首先進(jìn)入配置文件目錄
cd /etc/nginx
ls
# 查看配置文件
cat nginx.config
# 復(fù)制一份原始的配置文件
cp nginx.config nginx.config.back
# 按照上面 vue-router 的需求修改配置文件
vi nginx.config
# 進(jìn)入編輯狀態(tài)
I
# 修改文件
location / {
  try_files $uri $uri/ /index.html;
}
# 之后保存并退出
esc
:  
wq
# 再次查看是否已修改成功
cat nginx.config
# 重載 nginx 配置文件(必須重載,不然修改的是不會(huì)生效的叼耙!)
nginx -s reload
image.png
  • 上面的步驟操作完成之后便解決了 vue-router spa 帶來的刷新頁面 404 的問題了盆繁,哦耶~

16. 與后臺(tái) API 進(jìn)行通信時(shí),Content-Type 請求文本格式未統(tǒng)一帶來的問題

  • 問題截圖

image.png

后臺(tái)返回 415 Unsupported Media Type
對于當(dāng)前請求的方法和所請求的資源旬蟋,請求中提交的實(shí)體并不是服務(wù)器中所支持的格式油昂,因此請求被拒絕。

  • 產(chǎn)生錯(cuò)誤的原因:

這里使用的是 post 請求倾贰,后臺(tái)的請求文本格式為 Content-Type:application/json;charset=UTF-8
但是這里使用的是默認(rèn)的 Content-Type:application/x-www-form-urlencoded 所以造成的此次錯(cuò)誤

  • 解決方法

在提交數(shù)據(jù)之前使用 JSONstringify方法將數(shù)據(jù)轉(zhuǎn)換為 json 格式的文本即可冕碟,如下圖所示

image.png

JSON.stringiy()
JSON.stringify() 方法是將一個(gè)JavaScript值(對象或者數(shù)組)轉(zhuǎn)換為一個(gè) JSON字符串,如果指定了replacer是一個(gè)函數(shù)匆浙,則可以替換值安寺,或者如果指定了replacer是一個(gè)數(shù)組,可選的僅包括指定的屬性首尼。

17. 清除 .vue 文件在 vscode 編輯器中格式化時(shí)默認(rèn)添加的分號(hào)和雙引號(hào)的規(guī)則

參考地址

image.png

  • 問題

因?yàn)?vue-cli 項(xiàng)目創(chuàng)建后會(huì)默認(rèn)的增加 .eslintrc.js eslint 規(guī)則文件來幫助我們更好的統(tǒng)一代碼的規(guī)范性挑庶,但是現(xiàn)在的趨勢是省略 javascript 代碼書寫時(shí)在末尾添加的分號(hào),但是 vscode 編輯器因?yàn)檠b了 vetur這個(gè)插件软能,所以還是會(huì)像之前的那樣默認(rèn)追加迎捺,使得項(xiàng)目報(bào) eslint 語法的錯(cuò)誤,單雙引號(hào)也是相同的問題

  • 解決方法

先安裝擴(kuò)展插件 Prettier - Code formatter

image.png

之后在頂部菜單欄依次操作:【文件】->【首選項(xiàng)】->【設(shè)置】->【用戶設(shè)置】
最后增加下面的規(guī)則代碼片段

"prettier.singleQuote": true,
"prettier.semi": false

18. 之前的刪掉了查排,等待更新中......

19. 給 vue 掛載全局方法

  • 找到 main.js 文件進(jìn)行編輯凳枝,這里以 axios 為例演示
import Vue  from 'vue'
import  axios from 'axios'

Vue.prototype.axios = axios
  • 使用方法 某個(gè) .vue 文件的 sccript 中如下編輯
Vue.axios.post('url', { name: '' })
.then(response => { 
  console.log(response)
})
.catch(response => {
  console.log(response)
})
.finally(() => (me.loading= false));

20. axios 不兼容 ie 的問題解決

  • 問題描述
image.png

在 IE 瀏覽器下會(huì)報(bào) “Promise”未定義" 的錯(cuò)誤

  • 資料

axios npmjs 地址

image.png

問題解決參考地址

  • 解決方法

使用 babel-polyfill 這個(gè)包

  • 裝包
yarn add babel-polyfill --dev
  • 之后在 main.js 文件中引入包即可
import 'babel-polyfill'
image.png
  • 測試可兼容至 IE8+
image.png
  • 更新 優(yōu)化進(jìn)階 已經(jīng)過測試

參考文章里面有詳細(xì)的解釋:babel-polyfill使用與性能優(yōu)化
目的是為了打包出更小的體積,下圖是使用新方法打包出的體積跋核,用上面的方法打包出來是 2.64MB

image.png

  • 解決方法 將之前引入的 babel-polyfill 換成 core-js/es6/promise 這個(gè)是 vue-cli 腳手架就有的包無需再裝了
import 'core-js/es6/promise' // 解決 axios 兼容 IE 問題
image.png

21. 組件封裝岖瑰,這里以 bootstrap 的 modal 模塊為例

參考官方文檔 -- 組件基礎(chǔ)
參考官方文檔 -- 組件注冊
參考官方文檔 -- props

  • 先寫組件叛买,在 src -> components 目錄下新建一個(gè)文件夾 msgmodal -> index.vue,編輯如下
<template>
<!-- 彈窗 -->
    <div class="modal fade" tabindex="-1" role="dialog" id="myModal">
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <!-- <h5 class="modal-title"></h5> -->
            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
              <span aria-hidden="true">&times;</span>
            </button>
          </div>
          <div class="modal-body">
            <p>{{ modalMsg }}</p>
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-primary" data-dismiss="modal">確定</button>
            <button type="button" class="btn btn-secondary" data-dismiss="modal">關(guān)閉</button>
          </div>
        </div>
      </div>
    </div>
</template>

<script>
export default {
  name: 'MsgModal', // 定義的組件名稱 使用時(shí)寫法:msg-modal
  props: ['modalMsg'] // 定義的與父級(jí)通信的屬性值 使用時(shí)寫法:modal-msg
}
</script>

<style scoped>
</style>

  • 在具體的 .vue 文件使用組件蹋订,方法如下率挣,這里是用的動(dòng)態(tài)綁定的方法傳遞屬性的值
<!-- template -->
<!-- 彈窗 -->
    <msg-modal :modal-msg="modalMsg"></msg-modal>


// script
import MsgModal from '@/components/msgmodal'

export default{
  name:'App',
  components: { MsgModal },
  data () {
    return {
      // 彈窗信息 在執(zhí)行操作時(shí)使用
      modalMsg: ''
    }
  }
}
  • 測試,已實(shí)現(xiàn)可以實(shí)時(shí)更新內(nèi)容了
image.png

22. 在 router -> index.js 按需引入模塊露戒,優(yōu)化 SPA 頁面的性能

參考地址

  • 源碼
import Vue from 'vue'
import Router from 'vue-router'
import VueResource from 'vue-resource'

// import index from '@/index'
// import companyapply from '@/companyapply'
// import choosenumber from '@/choosenumber'
// import statelist from '@/statelist'

Vue.use(Router)
Vue.use(VueResource)

export default new Router({
  // 去掉路由中自帶的 #/ 這種東西
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'index',
      component: () => import('@/index')
    },
    {
      path: '/companyapply',
      name: 'companyapply',
      component: () => import('@/companyapply')
    },
    {
      path: '/choosenumber',
      name: 'choosenumber',
      component: () => import('@/choosenumber')
    },
    {
      path: '/statelist',
      name: 'statelist',
      component: () => import('@/statelist')
    }
  ]
})

23. 在 Vue 上掛載 vux 庫中的 LoadingPlugin 組件

官方文檔地址 - 該組件支持以plugin形式調(diào)用

  • main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import { LoadingPlugin } from 'vux' // 引入 looding 組件
import App from './App'
import store from './store' // 引入 vuex
import router from './router'

Vue.config.productionTip = false
Vue.http.options.emulateJSON = true

Vue.use(LoadingPlugin) // 掛載 loading 組件

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store, // 需要在添加
  components: { App },
  template: '<App/>'
})

  • .vue 文件中具體的使用示例:
// loading
this.$vux.loading.show({
  text: '數(shù)據(jù)提交中
})      

// 隱藏 loading
this.$vux.loading.hide()

24. 解決:使用 axios 默認(rèn)發(fā)送的是 application/json;charset=UTF-8 這種格式的數(shù)據(jù)后臺(tái)無法讀取的問題

后臺(tái)需要的是 application/x-www-form-urlencoded 這樣的數(shù)據(jù)格式
參考地址

  • 問題截圖
image.png
  • 需要的格式截圖
image.png
  • 解決方法

    • 使用 qs 模塊椒功,轉(zhuǎn)換數(shù)據(jù)格式
    image.png
    • 源碼
    import qs from 'qs' // 解決 axios 數(shù)據(jù)提交格式與后臺(tái)不一致的問題  -> name=hehe&age=10
    
    
    axios.post(postAPI,qs.stringify(postData))
    .then(request => {
      console.log(request)
    })
    .catch(error => {
      console.log(error)
    })
    

25. 化繁為簡的Watchers

原文地址

  • 場景還原:
created(){
    this.fetchPostList()
},
watch: {
    searchInputValue(){
        this.fetchPostList()
    }
}

組件創(chuàng)建的時(shí)候我們獲取一次列表,同時(shí)監(jiān)聽input框玫锋,每當(dāng)發(fā)生變化的時(shí)候重新獲取一次篩選后的列表這個(gè)場景很常見蛾茉,有沒有辦法優(yōu)化一下呢讼呢?

  • 招式解析:

首先撩鹿,在watchers中,可以直接使用函數(shù)的字面量名稱悦屏;其次节沦,聲明immediate:true表示創(chuàng)建組件時(shí)立馬執(zhí)行一次。

watch: {
    searchInputValue:{
        handler: 'fetchPostList',
        immediate: true
    }
}

26. 父子組件通信 -- 子組件可以調(diào)用父組件的方法

  • 實(shí)現(xiàn)思路

使用 this.$emit()

以下示例為分頁組件础爬,下面只是將主要的部分代碼貼出

  • 父組件
<template>
  <!-- 分頁 -->
      <pagination @updatePageData="loadPageData"></pagination>
</template>

<script>
  export default{
    methods:{
      loadPageData:function(){
        //  do something
      }
    }
  }
<script>
  • 上面代碼的說明

@updatePageData="loadPageData" :傳遞方法時(shí)前面使用 @ 甫贯;updatePageData 是給子組件使用的 父組件 loadPageData 方法的別名

  • 子組件
export default{
  methods:{
    pageGo:function(){
      const me = this
      // 調(diào)用父組件方法
      me.$emit('updatePageData')
    }
  }
}

27. 父子組件通信 -- 子組件可動(dòng)態(tài)獲取父組件的數(shù)據(jù)

  • 問題描述

由于父組件的數(shù)據(jù)是動(dòng)態(tài)獲取的,而子組件初始化時(shí)如果獲取不到數(shù)據(jù)就 Game Over

  • 實(shí)現(xiàn)思路

使用 props 傳數(shù)據(jù) 看蚜; watch 監(jiān)聽數(shù)據(jù)

以下示例為分頁組件叫搁,下面只是將主要的部分代碼貼出

  • 父組件
<template>
  <!-- 分頁 -->
      <pagination :parentPageData="pageGetData"></pagination>
</template>

<script>
  export default{
    data(){
      return{
        pageGetData:[]
      }
    },
    methods:{
      getData:function(){
        //  ajax 請求之后改變 pageGetData 的數(shù)據(jù)
      }
    }
  }
<script>
  • 上面代碼的說明

:parentPageData="pageGetData" 傳遞方法前面使用 :parentPageData 是給子組件使用的 父組件 pageGetData 數(shù)據(jù)的別名

  • 子組件
export default{
  props:['parentPageData'], // 父組件數(shù)據(jù) 別名
  watch:{
    // 監(jiān)聽父組件數(shù)據(jù)變化實(shí)時(shí)更新數(shù)據(jù)
    parentPageData:{
      handler: 'loadPageList',
      immediate: true
    }
  },
  methods:{
    // 加載頁面數(shù)據(jù)
    loadPageList:function(){
      // do something
    }
  }
}

28. vue 多頁面開發(fā)分頁組件 有搜索功能

文章筆記

29. Vue.vue 文件的樣式 style 標(biāo)簽中使用 background:url() 引入圖片

參考資料 vue 背景圖引入

  • 示例代碼片段
<style scoped>
.loading {
  position: fixed;
  left: 0;
  top: 0;
  background: url('~@/assets/img/loading-ball.svg') center center no-repeat #fff;
  width: 100vw;
  height: 100vh;
  z-index: 1000;
}
</style>

30. 頁面加載數(shù)據(jù)之前增加 loading 動(dòng)畫

文章筆記

31. 封裝一個(gè) axios 的通用方法

  • 思路

自定義一個(gè)函數(shù)供炎,將其掛載到 Vue 對象的 prototype 上面渴逻,方便在頁面使用,因?yàn)?axios 一般得和 qs 模塊配合使用
qs 使用參考地址

image.png

  • main.js 文件中掛載自定義方法
import Vue from 'vue'
import 'babel-polyfill' // 解決 axios 兼容 IE 問題
import qs from 'qs' // 解決 axios 數(shù)據(jù)提交格式與后臺(tái)不一致的問題  -> name=hehe&age=10
import axios from 'axios'
// import router from './router'
import '@/assets/css/common.css'
import App from './index.vue'

Vue.config.productionTip = false

/**
 * 自定義一個(gè)方法封裝 axios 請求音诫,并將其掛載至 Vue 原型鏈
 * @param {string} url axios 請求的地址
 * @param {string} dataJson axios 向請求地址發(fā)送的 json 數(shù)據(jù)
 * @param {function} sucessFn axios 成功回調(diào)函數(shù)
 * @param {function} errorFn axios 失敗回調(diào)函數(shù)
 */
Vue.prototype.axiosFn = function(url, dataJson, sucessFn, errorFn) {
  axios
    .post(url, qs.stringify(dataJson))
    .then(response => {
      console.log(response)
      sucessFn()
    })
    .catch(error => {
      console.log(error)
      errorFn()
    })
}

/* eslint-disable no-new */
new Vue({
  el: '#app',
  // router,
  components: { App },
  template: '<App/>'
})

  • 具體的使用就不寫了,只需要在調(diào)用方法 axiosFn 時(shí)給其傳相應(yīng)的參數(shù)即可

32. 滿足:點(diǎn)擊當(dāng)前元素時(shí),其子元素不會(huì)被點(diǎn)中滞欠,且還會(huì)執(zhí)行回調(diào)事件--修改當(dāng)前樣式

  1. 思路:

現(xiàn)在的需求是有一個(gè)列表燥滑,每個(gè)列表元素也是一個(gè)有子元素的嵌套元素,點(diǎn)擊列表的每個(gè)子元素時(shí)給當(dāng)前列表添加類名
正常使用官網(wǎng)提供的 事件處理 — Vue.js 文檔香罐,無法實(shí)現(xiàn)想要的效果卧波,因?yàn)辄c(diǎn)擊傳遞的 $event 是當(dāng)前點(diǎn)擊的事件,target 無法只得到列表的每個(gè)元素庇茫,相對其子元素而言就是父級(jí)元素
以前解決該方法的時(shí)候使用的是較為笨的方法:判斷 target 元素幽勒,將其強(qiáng)行指向列表元素這樣子

  1. 新方法

給每個(gè)列表元素里面的子元素設(shè)置 css 屬性 pointer-events: none;,這樣的話也無需給 @click 添加什么修飾符即可實(shí)現(xiàn)需求

33. 子頁面中在接口請求的時(shí)候港令,需要主頁面 App.vue 中請求到的接口的返回值啥容,特殊情況下會(huì)出現(xiàn) Bug

  1. 錯(cuò)誤重現(xiàn)

示例圖片

頂部是公用的導(dǎo)航組件锈颗,數(shù)據(jù)在 App.vue 中請求,之后存進(jìn) store 中去咪惠,因?yàn)槭?keep-alive 击吱,所以正常情況下頂部的數(shù)據(jù)在第一個(gè)頁面載入時(shí)便會(huì)儲(chǔ)存到 store 中,但是如果是第二個(gè)頁面(因?yàn)橹挥羞@個(gè)頁面會(huì)需要上面的聯(lián)系電話的數(shù)據(jù))ctrl+f5 強(qiáng)制刷新后遥昧,store 中的數(shù)據(jù)會(huì)被清空覆醇,這樣如果在子頁面請求時(shí)沒有及時(shí)的將主頁面中請求到的數(shù)據(jù)聯(lián)系電話存入 store 中的話,便會(huì)出現(xiàn)子頁面請求時(shí)獲取聯(lián)系電話字段為空的情況炭臭,主要就是因?yàn)檎埱笫峭桨l(fā)生的永脓,加載的延遲很低,應(yīng)該是主頁面請求完成之后子頁面再請求這樣子鞋仍。

  1. 解決 bug 后的源碼示例 App.vue 文件
<template lang="pug">
#app
  .header-box(v-loading="layoutHeaderLoading")
    LayoutHeader
  .nav-box
    LayoutNav
  keep-alive
    .router-view(v-if="loadGuestRecord")
      router-view
</template>

<script>
import LayoutNav from "@/components/LayoutNav";
import LayoutHeader from "@/components/LayoutHeader";
import { layoutHeader } from "@/api";

export default {
  components: { LayoutNav, LayoutHeader },
  data() {
    return {
      // 是否加載 loading
      layoutHeaderLoading: false,
      // 頂部 header 數(shù)據(jù)對象
      headerInformation: {},
      // 解決 “聯(lián)系電話”未儲(chǔ)存到 store 中常摧,所產(chǎn)生的 bug
      loadGuestRecord: true
    };
  },
  created() {
    const me = this;
    me.getPageData();
  },
  watch: {
    $route(to, from) {
      const me = this;
      // 對路由變化作出響應(yīng)...
      to.path === "/guestRecord" &&
        !me.$store.getters.getMobile &&
        (me.loadGuestRecord = false);
    }
  },
  methods: {
    /**
     * 獲取頂部 header 數(shù)據(jù)
     */
    getPageData() {
      const me = this;
      me.layoutHeaderLoading = true;
      layoutHeader
        .getCusUserInfo()
        .then(resolve => {
          console.log(resolve);
          resolve.data.code === 200 &&
            (me.headerInformation = resolve.data.data);
          me.$store.commit("updateMobile", me.headerInformation.mobile);
        })
        .catch(error => {
          console.log(error);
        })
        .finally(() => {
          me.layoutHeaderLoading = false;
          !me.loadGuestRecord && (me.loadGuestRecord = true);
        });
    }
  }
};
</script>

  1. 解決思路,解釋上面的代碼

主要是頂部的全局請求.then( 單頁請求 )這個(gè)樣子的威创,下面是具體的實(shí)施方案:
在主頁面 data 中增加一個(gè)布爾變量落午,默認(rèn)為true,用它來指令是否 v-if 加載子頁面 router-view 肚豺,watch.$route 中判斷路由以及其他條件溃斋,為否 布爾變量 = false,再在主頁面的請求中 .finally( 布爾變量 = true )

34. router-link 導(dǎo)航接收并傳遞全部的 url 參數(shù)

  1. 先說思路:

在主頁面 App.vue 文件中吸申,接收所有的參數(shù)并將其傳遞給 nav 導(dǎo)航

  1. 源碼示例:
  • 主文件 App.vue
<template lang="pug">
#app
  .header-box(v-loading="layoutHeaderLoading")
    LayoutHeader(:header-information="headerInformation")
  .nav-box
    LayoutNav(:url-query="urlQuery")
  keep-alive
    .router-view(v-if="loadGuestRecord")
      router-view
</template>

<script>
import LayoutNav from "@/components/LayoutNav";
import LayoutHeader from "@/components/LayoutHeader";
import { layoutHeader } from "@/api";

export default {
  components: { LayoutNav, LayoutHeader },
  data() {
    return {
      // 是否加載 loading
      layoutHeaderLoading: false,
      // 頂部 header 數(shù)據(jù)對象
      headerInformation: {},
      // 接受頁面的所有參數(shù)梗劫,并將其賦值到 router-link 參數(shù)上面
      urlQuery: {}
    };
  },
  created() {
    const me = this;
    me.getPageData();
  },
  watch: {
    $route(to, from) {
      const me = this;
      me.urlQuery = to.query;
    }
  }
};
</script>

  • 組件 LayoutNav.vue
<template lang="pug">
nav.nav
  ul.nav-list
    li.nav-item(v-for="(item,index) in routers" :key="item.id")
      router-link(:to="{ name: item.link, query: urlQuery }")
        .nav-icon
          i(:class="item.icon")
        h3.nav-title {{ item.name }}
</template>

<script>
import { routers } from "./config.json";

export default {
  name: "layoutNav",
  props: {
    urlQuery: Object
  },
  data() {
    return {
      routers: routers
    };
  }
};
</script>

35. 項(xiàng)目增加權(quán)限

  1. 思路:
  • 首先做的項(xiàng)目是從別的項(xiàng)目中跳轉(zhuǎn)過來的,所以有權(quán)限的需求
  • 判斷權(quán)限的主要步驟:
    • 先增加一個(gè) 401 無權(quán)限頁面
    • 注冊一個(gè)全局前置守衛(wèi)
    • 在守衛(wèi)中判斷加密字符是否一致截碴,路由跳轉(zhuǎn)至相應(yīng)的頁面去
    • App.vue 文件中梳侨,使用全局的組件時(shí),增加一個(gè) 布爾變量 控制是否展示組件隐岛,默認(rèn)為 false 展示組件猫妙,true 是不展示組件,如果 watch.$route.to.path === "/401" 聚凹,這個(gè) 布爾變量 便為 true

36. 使用 beforeRouteLeave 銷毀組件割坠,實(shí)現(xiàn)組件的實(shí)時(shí)化

  • 代碼
  // 導(dǎo)航離開該組件的對應(yīng)路由時(shí)調(diào)用 [可以訪問組件實(shí)例 `this`] https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E7%BB%84%E4%BB%B6%E5%86%85%E7%9A%84%E5%AE%88%E5%8D%AB
  beforeRouteLeave(to, from, next) {
    // 銷毀組件,避免通過 vue-router 再次進(jìn)入時(shí)妒牙,仍是上次的 history 緩存的狀態(tài)
    this.$destroy(true);
    next();
  }

37. 使用官方提供的 component彼哼,根據(jù)條件循環(huán)判斷使用不同的組件

<template lang="pug">
  .integrated-query
    van-tabs(v-model="tabActive" color="#39a0ff" line-width="40" sticky)
      van-tab(v-for="item in tabTitleList" :key="item.value" :title="item.name" :name="item.value")
        component(:is="item.value")
</template>

<script>

export default {
  
  name: "integratedQuery",
  components: {
    Overview: () => import("./components/Overview"),
    Product: () => import("./components/Product"),
    Accounting: () => import("./components/Accounting"),
    Order: () => import("./components/Order"),
    Assets: () => import("./components/Assets"),
    Discount: () => import("./components/Discount"),
    Invoice: () => import("./components/Invoice"),
    VirtualNetwork: () => import("./components/VirtualNetwork")
  },
  data() {
    return {
      tabTitleList: [
        {
          name: "總覽",
          value: "overview"
        },
        {
          name: "產(chǎn)品",
          value: "product"
        },
        {
          name: "賬務(wù)",
          value: "accounting"
        },
        {
          name: "訂單",
          value: "order"
        },
        {
          name: "資產(chǎn)",
          value: "assets"
        },
        {
          name: "優(yōu)惠",
          value: "discount"
        },
        {
          name: "發(fā)票",
          value: "invoice"
        },
        {
          name: "虛擬網(wǎng)",
          value: "virtual-network"
        }
      ],
      tabActive: "overview"
    };
  },
}
</script>

38. 基于 vue-cli 腳手架 官方配置 在配置文件中 vue.config.js 引入樣式預(yù)處理器共享的全局變量。

  • 配置方式
module.exports = {
  /**
   * css 相關(guān)配置
   */
  css: {
    loaderOptions: {
      stylus: {
        import: "~@/common/stylus/mixin.styl"
      }
    }
  }
}

39. el-input 使用 @keyup.enter 無效

需要添加 .native 修飾符

  • 代碼示例
el-input(placeholder="請輸入內(nèi)容" v-model="dataListQuery.queryKey" size="small" clearable @keyup.enter.native="getDataList(leftTreeSelect.id)")

40. el-table 使用 toggleAllSelection 方法無效

需要在函數(shù)中湘今,增加一個(gè)參數(shù) true

  • 代碼示例
this.$refs.dialogTable.toggleAllSelection(true)

41. el-select 選中之后敢朱,數(shù)據(jù)無法回顯

  • 需求描述

目前是有一個(gè)彈窗,內(nèi)部有一個(gè)表格,表格內(nèi)部的每一條數(shù)據(jù)都是可以單獨(dú)編輯的拴签,有 input 也有 select

image.png

  • 問題描述

當(dāng)我選中維表或者賬期下拉選項(xiàng)之后孝常,相對 v-model 綁定的數(shù)據(jù)字段已經(jīng)成功更新了,但是在 el-select 組件中顯示不出來字典相應(yīng)的文本

  • 問題解決

經(jīng)過幾個(gè)小時(shí)的反復(fù)試驗(yàn)蚓哩,最終確定是由于我綁定的數(shù)據(jù)字段构灸,并不是在數(shù)據(jù)初始化時(shí)(接口獲取的時(shí)候)定義的,所導(dǎo)致的字段無法及時(shí)更新的問題岸梨。

  • 下面簡單描述下我具體的錯(cuò)誤方式:

接口獲取完數(shù)據(jù)喜颁,我并沒有在這個(gè)時(shí)候增加一些需要初始化的字段,而是選擇在將接口數(shù)據(jù)賦值給彈窗子組件的 form 之后曹阔,再在彈窗子組件中進(jìn)行了字段的初始化半开,由于我每次賦值都是使用的 ES6 對象解構(gòu)賦值的方式,所以最終導(dǎo)致解構(gòu)出的變量已經(jīng)不是最初始賦值時(shí)的對象赃份,也就無法同步更新 form 表單中的數(shù)據(jù)寂拆。
所以最終的解決方法就是,在接口獲取完數(shù)據(jù)芥炭,當(dāng)時(shí)就增加一些初始化必要的字段漓库。

42. 父組件使用 v-model 傳值給子組件恃慧,實(shí)現(xiàn)子組件可以同步更新父組件的 v-model 綁定的值

  1. 參考資料

官方提供的 model API
csdn vue 父子組件使用v-model通信

  1. 具體實(shí)現(xiàn)的主要代碼
  • 父組件
<template lang="pug">
  .content
    child-el(v-model="data")
</template>

<script>
import "childEl" from "./components"

export default {
  components: {
    childEl
  },
  data() {
    return {
      data: ""
    }
  }
}
</script>
  • 子組件
<template lang="pug">
  .content
    .text(@click="handleClick") {{ data }}
</template>

<script>
export default {
  model: {
    prop: "data",
    event: 'change'
  },
  props: {
    data: {
      type: String,
      default: ""
    }
  },
  methods: {
    handleClick() {
      this.$emit("change", "測試文本")
    }
  }
}
</script>

43. 當(dāng)使用 v-for 循環(huán)生成 dom 時(shí)园蝠,當(dāng)需要再增加 ref 屬性時(shí),獲取組件時(shí)需要加一個(gè)下標(biāo) [0]

  1. 代碼

      //- 組件容器
      .tab-content(v-for='item in navListData', :key='item.value')
        .tab-bar {{ item.name }}
        component(:is='item.value', :ref='item.value')
const componentsList = ['Overview', 'Volume', 'Industry', 'Monographic']
      componentsList.forEach((element) => {
        const component = this.$refs[`${element}`]
        console.log(component)
        component && component.length && component[0].getPageData(this.pagePostData)
      })

44. 偶發(fā)事件痢士,在 v-if 判斷較多彪薛,層級(jí)嵌套較深內(nèi)部也有 v-if 或者 v-show 時(shí),會(huì)出現(xiàn)祖先級(jí) dom 可以渲染怠蹂,但是內(nèi)部的所有子級(jí) dom 無法正常渲染的 bug

這里我暫時(shí)采用的解決方案是將祖先級(jí)元素的 v-if 修改為 v-show

45. 使用 vuex 進(jìn)行數(shù)據(jù)更新的時(shí)候善延,如果 val 是沒有變化的 set,那么是不會(huì)被 watch 捕獲到的

<template lang="pug">
//- 通用的 下載文件過大城侧,提示彈窗
.download-tip
  //- 彈窗內(nèi)容
  el-dialog(
    title='下載提示',
    width='450px',
    :visible.sync='dialogVisible',
    :append-to-body='true',
    @close='SetDownloadTipVisible(false)'
  )
    .dialog-content
      .dialog-details
        .dialog-text 由于當(dāng)前數(shù)據(jù)下載量過大易遣,系統(tǒng)將采取異步下載方式,#[br] 請到 #[el-button.color-blue(type='text', @click='checkDownloadTask') 下載界面] 查看下載進(jìn)度嫌佑。
      .dialog-button
        el-button(
          type='primary',
          size='small',
          @click='SetDownloadTipVisible(false)'
        ) 我知道了
</template>

<script>
import { mapGetters, mapActions } from 'vuex'

export default {
  name: 'DownloadTip',
  data() {
    return {
      dialogVisible: false
    }
  },
  computed: {
    ...mapGetters(['downloadTipVisible'])
  },
  watch: {
    // 監(jiān)測 store 中的 downloadTipVisible豆茫,實(shí)時(shí) 顯示/關(guān)閉 彈窗
    downloadTipVisible: {
      handler(val) {
        this.dialogVisible = val
      }
    }
  },
  methods: {
    ...mapActions(['SetDownloadTaskVisible', 'SetDownloadTipVisible']),
    /**
     * 查看 通用下載任務(wù)列表
     */
    checkDownloadTask() {
      this.SetDownloadTipVisible(false)
      this.SetDownloadTaskVisible(true)
    }
  }
}
</script>

<style lang="stylus" scoped>
.el-dialog__wrapper
  >>>
    .el-dialog__body
      padding 0
    .el-dialog__header, .el-dialog__footer
      display none
    .dialog-content
      padding 245px 40px 20px
      background url('~@/assets/common/img-download.png') top center no-repeat
      background-size 100% auto
      text-align center
      .dialog-details
        color #999
        line-height 22px
        .el-button--text
          padding 0
      .dialog-button
        padding 30px 0
</style>

以上述代碼為例:downloadTipVisiblestore 中的變量,如果它的當(dāng)前的值是 false屋摇,那么 SetDownloadTipVisible(false) 時(shí)揩魂,watch 中時(shí)無法捕獲并及時(shí)更新的,所以需要在 store 初始化時(shí)炮温,將 downloadTipVisible 設(shè)置為 falsedialogVisible 一致火脉,這樣它們才能實(shí)時(shí)同步。(2021.7.12 續(xù):但是這樣好像也沒必要多聲明一個(gè) dialogVisible 了 /汗)

45. 利用 .sync 修飾符,讓子組件更新父組件的值倦挂,常用于彈窗組件的顯示/隱藏

參開文章:vue 實(shí)戰(zhàn)中的一些小魔法 - 前端大全

  1. 主要的源碼部分示例:
  • 父組件
<template lang="pug">
.data-lifecycle-preparedness
  .page-content(v-show='!performDetailsShow')
  perform-details(
    v-if='performDetailsShow',
    v-model='detailsData',
    :perform-details-show.sync='performDetailsShow'
  )
</template>

<script>
export default {
  name: 'dataLifecycleIndex',
  data() {
    return {
      performDetailsShow: false,
      detailsData: {
          key: 'test'
        }
    }
}
</script>
  • 子組件
<template lang="pug">
.perform-details
  .page-content
    el-button.is-active(
      plain,
      icon='el-icon-back',
      size='small',
      @click='backClickHandler'
     ) 返回
</template>

<script>
export default {
  name: 'PerformDetails',
  model: {
    prop: 'detailsData',
    event: 'change'
  },
  props: {
    detailsData: {
      type: Object,
      default: () => {}
    },
    performDetailsShow: {
      type: Boolean,
      default: true
    }
  },
  methods: {
    /**
     * 返回 按鈕 點(diǎn)擊回調(diào)
     */
    backClickHandler() {
      this.$emit('update:performDetailsShow', false)
      this.$emit('change', {})
    }
  }
}
</script>

是的畸颅,示例中不止使用了 .sync 還有常用的 v-model

46. 某些特定的時(shí)候,會(huì)有需要同時(shí) watch 兩個(gè)不同的屬性的需求方援,這時(shí)可以使用 computed 新增屬性來實(shí)現(xiàn)

  • 具體實(shí)現(xiàn)的主要代碼實(shí)例
import { mapGetters, mapActions } from 'vuex'
import commonAPI from '@/api/common'

export default {
  computed: {
    ...mapGetters(['sidebar', 'commonPageData', 'tabInfo', 'activeMenuId']),
    // 雖然最終想要的結(jié)果是實(shí)現(xiàn)了重斑,但是 watch 時(shí)會(huì)執(zhí)行兩次,請求兩次接口肯骇,因?yàn)?isChangeReport 會(huì)改變兩次
    isChangeReport() {
      const { $route, activeMenuId } = this
      return {
        $route,
        activeMenuId
      }
    }
  },
  watch: {
    isChangeReport: {
      handler(val) {
        console.log(val)
        const { $route, activeMenuId } = this
        if (activeMenuId && !$route.path.includes('serviceAnalysisCommon')) {
          commonAPI.getMenuDetailsData(activeMenuId).then((res) => {
            const { description } = res
            this.SetCommonPageData({
              title: description ? '報(bào)表介紹' : '',
              content: description || ''
            })
          })
        } else {
          this.SetCommonPageData({
            title: '',
            content: ''
          })
        }
      },
      immediate: true
    }
  },
  methods: {
    ...mapActions(['SetCommonPageData'])
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末窥浪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子笛丙,更是在濱河造成了極大的恐慌漾脂,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胚鸯,死亡現(xiàn)場離奇詭異骨稿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)姜钳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門坦冠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人哥桥,你說我怎么就攤上這事辙浑。” “怎么了拟糕?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵判呕,是天一觀的道長。 經(jīng)常有香客問我送滞,道長侠草,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任犁嗅,我火速辦了婚禮边涕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘褂微。我一直安慰自己功蜓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布蕊梧。 她就那樣靜靜地躺著霞赫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪肥矢。 梳的紋絲不亂的頭發(fā)上端衰,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天叠洗,我揣著相機(jī)與錄音,去河邊找鬼旅东。 笑死灭抑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的抵代。 我是一名探鬼主播腾节,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼荤牍!你這毒婦竟也來了案腺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤康吵,失蹤者是張志新(化名)和其女友劉穎劈榨,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晦嵌,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡同辣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了惭载。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旱函。...
    茶點(diǎn)故事閱讀 39,703評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖描滔,靈堂內(nèi)的尸體忽然破棺而出棒妨,到底是詐尸還是另有隱情,我是刑警寧澤伴挚,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布靶衍,位于F島的核電站灾炭,受9級(jí)特大地震影響茎芋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蜈出,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一田弥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧铡原,春花似錦偷厦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至卵洗,卻和暖如春请唱,著一層夾襖步出監(jiān)牢的瞬間弥咪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工十绑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留聚至,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓本橙,卻偏偏與公主長得像扳躬,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子甚亭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評論 2 353