摘要:由于公司的前端開始轉向,最近開始使用這個框架進行開發,遇到一些問題記錄下來,以備后用。查了一下,發現可能是打包或是資源引用問題,目前該問題還未被妥善處理,需要通過一些來解決這個問題。為解決這個問題,中提供了方法對象受現
由于公司的前端開始轉向 VueJS,最近開始使用這個框架進行開發,遇到一些問題記錄下來,以備后用。
主要寫一些 官方手冊 上沒有寫,但是實際開發中會遇到的問題,需要一定知識基礎。
涉及技術棧CLI: Vue-CLI
UI: Element
HTML: Pug(Jade)
CSS: Less
JavaScript: ES6
正文:
polyfill 與 transform-runtime首先,vue-cli 為我們自動添加了 babel-plugin-transform-runtime 這個插件,該插件多數情況下都運作正常,可以轉換大部分 ES6 語法。
但是,存在如下兩個問題:
異步加載組件時,會產生 polyfill 代碼冗余
不支持對全局函數與實例方法的 polyfill
兩個問題的原因均歸因于 babel-plugin-transform-runtime 采用了沙箱機制來編譯我們的代碼(即:不修改宿主環境的內置對象)。
由于異步組件最終會被編譯為一個多帶帶的文件,所以即使多個組件中使用了同一個新特性(例如:Object.keys()),那么在每個編譯后的文件中都會有一份該新特性的 polyfill 拷貝。如果項目較小可以考慮不使用異步加載,但是首屏的壓力會比較大。
不支持全局函數(如:Promise、Set、Map),Set 跟 Map 這兩種數據結構應該大家用的也不多,影響較小。但是 Promise 影響可能就比較大了。
不支持實例方法(如:"abc".includes("b")、["1", "2", "3"].find((n) => n < 2) 等等),這個限制幾乎廢掉了大部分字符串和一半左右數組的新特性。
替換 babel-polyfill一般情況下 babel-plugin-transform-runtime 能滿足大部分的需求,當不滿足需求時,推薦使用完整的 babel-polyfill。
首先,從項目中移除 babel-plugin-transform-runtime
卸載該依賴:
npm un babel-plugin-transform-runtime -D
修改 babel 配置文件
// .babelrc { //... "plugins": [ // - "transform-runtime" ] //... }
然后,安裝 babel-polyfill 依賴:
npm i babel-polyfill -D
最后,在入口文件中導入
// src/main.js import "babel-polyfill"ES6 import 引用問題
在 ES6 中,模塊系統的導入與導出采用的是引用導出與導入(非簡單數據類型),也就是說,如果在一個模塊中定義了一個對象并導出,在其他模塊中導入使用時,導入的其實是一個變量引用(指針),如果修改了對象中的屬性,會影響到其他模塊的使用。
通常情況下,系統體量不大時,我們可以使用 JSON.parse(JSON.stringify(str)) 簡單粗暴地來生成一個全新的深度拷貝的 數據對象。不過當組件較多、數據對象復用程度較高時,很明顯會產生性能問題,這時我們可以考慮使用 Immutable.js。
在 Vue 中使用 Pug 與 Less 安裝依賴鑒于這個原因,進行復雜數據類型的導出時,需要注意多個組件導入同一個數據對象時修改數據后可能產生的問題。
此外,模塊定義變量或函數時即便使用 let 而不是 const,在導入使用時都會變成只讀,不能重新賦值,效果等同于用 const 聲明。
Vue 中使用 vue-loader 根據 lang 屬性自動判斷所需要的 loader,所以不用額外配置 Loader,但是需要手動安裝相關依賴:
npm i pug -D npm i less-loader -D
還是相當方便的,不用手動修改 webpack 的配置文件添加 loader 就可以使用了
使用使用 pug 還是 pug-loader?sass 兩種語法的 loader 如何設置?
--- 請參考 預處理器 · vue-loader
.action(v-if="hasRight") ul li 編輯 li 刪除定義全局函數或變量
許多時候我們需要定義一些全局函數或變量,來處理一些頻繁的操作(這里拿 AJAX 的異常處理舉例說明)。但是在 Vue 中,每一個單文件組件都有一個獨立的上下文(this)。通常在異常處理中,需要在視圖上有所體現,這個時候我們就需要訪問 this 對象,但是全局函數的上下文通常是 window,這時候就需要一些特殊處理了。
簡單粗暴型最簡單的方法就是直接在 window 對象上定義一個全局方法,在組件內使用的時候用 bind、call 或 apply 來改變上下文。
定義一個全局異常處理方法:
// errHandler.js window.errHandler = function () { // 不能使用箭頭函數 if (err.code && err.code !== 200) { this.$store.commit("err", true) } else { // ... } }
在入口文件中導入:
// src/main.js import "errHandler.js"
在組件中使用:
// xxx.vue export default { created () { this.errHandler = window.errHandler.bind(this) }, method: { getXXX () { this.$http.get("xxx/xx").then(({ body: result }) => { if (result.code === 200) { // ... } else { this.errHandler(result) } }).catch(this.errHandler) } } }優雅安全型
在大型多人協作的項目中,污染 window 對象還是不太妥當的。特別是一些比較有個人特色的全局方法(可能在你寫的組件中幾乎處處用到,但是對于其他人來說可能并不需要)。這時候推薦寫一個模塊,更優雅安全,也比較自然,唯一不足之處就是每個需要使用該函數或方法的組件都需要進行導入。
使用方法與前一種大同小異,就不多作介紹了。 ̄ω ̄=
Moment.JS 與 Webpack在使用 Moment.js 遇到一些問題,發現最終打包的文件中將 Moment.js 的全部語言包都打包了,導致最終文件徒然增加 100+kB。查了一下,發現可能是 webpack 打包或是 Moment.js 資源引用問題(?),目前該問題還未被妥善處理,需要通過一些 trick 來解決這個問題。
在 webpack 的生產配置文件中的 plugins 字段中添加一個插件,使用內置的方法類 ContextReplacementPlugin 過濾掉 Moment.js 中那些用不到的語言包:
// build/webpack.prod.conf.js new webpack.ContextReplacementPlugin(/moment[/]locale$/, /^./(zh-cn)$/)
自定義路徑別名解決方案采自 oleg-nogin@webpack/webpack#3128。
問題討論詳見 GitHub Issue: moment/moment#2373、webpack/webpack#3128。
可能有些人注意到了,在 vue-cli 生成的模板中在導入組件時使用了這樣的語法:
import Index from "@/components/Index"
這個 @ 是什么東西?后來改配置文件的時候發現這個是 webpack 的配置選項之一:路徑別名。
我們也可以在基礎配置文件中添加自己的路徑別名,比如下面這個就把 ~ 設置為路徑 src/components 的別名:
// build/webpack.base.js { resolve: { extensions: [".js", ".vue", ".json"], alias: { "vue$": "vue/dist/vue.esm.js", "@": resolve("src"), "~": resolve("src/components") } } }
然后我們導入組件的時候就可以這樣寫:
// import YourComponent from "YourComponent" // import YourComponent from "./YourComponent" // import YourComponent from "../YourComponent" // import YourComponent from "/src/components/YourComponent" import YourComponent from "~/YourComponent"
既解決了路徑過長的麻煩,又解決了相對路徑的煩惱,方便很多吧!ヾ(???ゞ)
CSS 作用域與模塊 組件內樣式通常,組件中 標簽里的樣式是全局的,在使用第三方 UI 庫(如:Element)時,全局樣式很可能影響 UI 庫的樣式。
我們可以通過添加 scoped 屬性來使 style 中的樣式只作用于當前組件:
導入樣式在有 scoped 屬性的 style 標簽內導入其他樣式,同樣會受限于作用域,變為組件內樣式。復用程度較高的樣式不建議這樣使用。
另,在組件內樣式中應避免使用元素選擇器,原因在于元素選擇器與屬性選擇器組合時,性能會大大降低。
--- 兩種組合選擇器的測試:classes selector,elements selector
相對于 style 使用 scoped 屬性時的組件內樣式,有時候我們也需要添加一些全局樣式。當然我們可以用沒有 scoped 屬性的 style 來寫全局樣式。
但是相比較,更推薦下面這種寫法:
/* 多帶帶的全局樣式文件 */ /* style-global.less */ body { font-size: 10px; } .title { font-size: 1.4rem; font-weight: bolder; }
然后在入口文件中導入全局樣式:
// src/main.js import "style-global.less"獲取表單控件值
通常我們可以直接使用 v-model 將表單控件與數據進行綁定,但是有時候我們也會需要在用戶輸入的時候獲取當前值(比如:實時驗證當前輸入控件內容的有效性)。
這時我們可以使用 @input 或 @change 事件綁定我們自己的處理函數,并傳入 $event 對象以獲取當前控件的輸入值:
change (e) { let curVal = e.target.value if (/^d+$/.test(curVal)) { this.num = +curVal } else { console.error("%s is not a number!", curVal) } }
v-for 的使用 tips當然,如果 UI 框架采用 Element 會更簡單,它的事件回調會直接傳入當前值。
v-for 指令很強大,它不僅可以用來遍歷數組、對象,甚至可以遍歷一個數字或字符串。
基本語法就不講了,這里講個小 tips:
索引值在使用 v-for 根據對象或數組生成 DOM 時,有時候需要知道當前的索引。我們可以這樣:
但是,在遍歷數字的時候需要注意,數字的 value 是從 1 開始,而 key 是從 0 開始:
模板的唯一根節點2.2.0+ 的版本里,當在組件中使用 v-for 時,key 現在是必須的。
與 JSX 相同,組件中的模板只能有一個根節點,即下面這種寫法是 錯誤 的:
Title
Balabala...
我們需要用一個塊級元素把他包裹起來:
Title
Balabala...
項目路徑配置原因參考:React-小記:組件開發注意事項#唯一根節點
由于 vue-cli 配置的項目提供了一個內置的靜態服務器,在開發階段基本不會有什么問題。但是,當我們把代碼放到服務器上時,經常會遇到靜態資源引用錯誤,導致界面一片空白的問題。
這是由于 vue-cli 默認配置的 webpack 是以站點根目錄引用的文件,然而有時候我們可能需要把項目部署到子目錄中。
我們可以通過 config/index.js 來修改文件引用的相對路徑:
build.assetsSubDirectory: "static" build.assetsPublicPath: "/" dev.assetsSubDirectory: "static" dev.assetsPublicPath: "/"
我們可以看到導出對象中 build 與 dev 均有 assetsSubDirectory、assetsPublicPath 這兩個屬性。
其中 assetsSubDirectory 指靜態資源文件夾,也就是打包后的 js、css、圖片等文件所放置的文件夾,這個默認一般不會有問題。
assetsPublicPath 指靜態資源的引用路徑,默認配置為 /,即網站根目錄,與 assetsSubDirectory 組合起來就是完整的靜態資源引用路徑 /static。
寫到這里解決方法已經很明顯了,只要把根目錄改為相對目錄就好了:
build.assetsSubDirectory: "static" build.assetsPublicPath: "./"
沒錯!就是一個 . 的問題。ㄟ( ▔, ▔ )ㄏ
更小的 Polyfill 開銷 在引入 Polyfill 之后,可以在 .babelrc 文件中開啟 useBulitIns 屬性。啟用該屬性后,編譯項目時會根據項目中新特性的使用情況將完整的 polyfill 拆分成獨立的模塊序列。
啟用 useBuiltIns 屬性:
// .babelrc { "presets": [ ["env", { "modules": false, "useBuiltIns": true }], "es2015", "stage-2" ] // ... }
安裝后引入 babel-polyfill:
// src/main.js import "babel-polyfill" [1, 2, 3].find((v => v > 2))
啟用 useBulitIns 后自動拆分 babel-polyfill
import "core-js/modules/es6.array.find" [1, 2, 3].find((v => v > 2))
使用 ESnext class 特性 對比經測試最大減少了一半左右的 polyfill 體積
沒深入研究哈,猜測可能加了 core-js 跟一些基礎的 polyfill
默認時,Vue 單文件組件使用一個對象來描述組件內部的實現:
const App = { // initialized data data () { return { init: false } } // lifecycle hook created () {} mounted () {} // ... } export default App
我們可以通過安裝一些依賴來支持最新的 class 寫法:
import Vue from "vue" import Component from "vue-class-component" @Component class App extends Vue { init = false; created () {} mounted () {} } export default App
使用不可否認,確實多些了一些代碼哈,不過個人還是比較傾向新語法特性的寫法的,畢竟標準即是燈塔
P.S 這里使用了還處于 Stage 3 的 Field declarations 來聲明組件的初始 data
下面來看看需要做哪些改動以支持使用 class 的寫法:
首先,最明顯的就是我們需要 vue-class-component 這個依賴了。
然后,這個依賴需要 babel 的 transform-decorators-legacy 插件支持。
最后,如果你也想使用 Field declarations 字段聲明寫法,再添加一個 transform-class-properties 就好了。
安裝依賴:
npm i vue-class-component -D npm i babel-plugin-transform-decorators-legacy -D npm i babel-plugin-transform-class-properties -D
配置 babel
// .babelrc { // ... "plugins": [ "transform-runtime", "transform-decorators-legacy", "transform-class-properties" ] // ... }
響應式數據失效 數組注意:transform-decorators-legacy 需放在 transform-class-properties 之前
由于 Vue.js 響應式數據依賴于對象方法 Object.defineProperty。但很明顯,數組這個特殊的“對象”并沒有這個方法,自然也無法設置對象屬性的 descriptor,從而也就沒有 getter() 和 setter() 方法。所以在使用數組索引角標的形式更改元素數據時(arr[index] = newVal),視圖往往無法響應式更新。
為解決這個問題,Vue.js 中提供了 $set() 方法:
vm.arr.$set(0, "newVal") // vm.arr[0] = "newVal"對象
受現代 JavaScript 的限制(以及廢棄 Object.observe),Vue 不能檢測到對象屬性的添加或刪除。由于 Vue 會在初始化實例時對屬性執行 getter/setter 轉化過程,所以屬性必須在 data 對象上存在才能讓 Vue 轉換它,這樣才能讓它是響應的。
Ref: 深入響應式原理 - Vue.js
var vm = new Vue({ data: { a: 1 } }) // `vm.a` 是響應的 vm.b = 2 // `vm.b` 是非響應的靜態類型檢測
推薦在開發較復雜的組件時使用 props 靜態類型檢測,提高組件的健壯性,多數情況下可以在轉碼階段提前發現錯誤。
// before prop: [ "id", "multiple", "callback", ]
// after props: { id: { type: [ Number, Array ], required: true, }, multiple: { type: Boolean, default: false, }, callback : Function, }異步組件
使用處于 Stage.3 階段的動態導入函數 import(),同時借助 webpack 的代碼分割功能,在 Vue.js 中我們可以很輕松地實現一個異步組件。
異步路由組件const AsyncComponent = () => import("./AsyncComponent")異步組件工廠
Vue.component( "async-webpack-example", () => import("./my-async-component") )
相比于異步路由組建,異步組件工廠一般適用于組件內進一步小顆粒度的拆分處理,如:大體量組件內初次加載時的非必要組件(組件內嵌套的彈窗組件或 Popover 組件等)。
To be continue...
文章還在完善中,歡迎大家一起討論 Vue.JS 開發中遇到的一些問題哈 (?▽?)/
看看你的收藏列表,你確定收藏了會記得看嗎_(:зゝ∠)_
讀一讀開發的時候至少會有個印象,點個贊打卡啦~原文:VueJS 開發常見問題集錦
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/84098.html
摘要:前端日報精選開發常見問題集錦前端碼農的自我修養虛擬內部是如何工作的譯知乎專欄并不慢,只是你使用姿勢不對一份優化指南掘金老司機帶你秒懂內存管理第一部中文免費公開課前端面試的大關鍵點,你到了嗎知乎專欄高效開發與設計姐的圖片二三 2017-07-19 前端日報 精選 VueJS 開發常見問題集錦 - 前端碼農的自我修養 - SegmentFault虛擬 DOM 內部是如何工作的?[譯]Hig...
摘要:然而這次的文章,就像賀師俊所說的這篇文章是從程序員這個老年度總結前端掘金年對我來說,是重要的一年。博客導讀總結個人感悟掘金此文著筆之時,已經在眼前了。今天,我就來整理一篇,我個人認為的年對開發有年終總結掘金又到 2016 Top 10 Android Library - 掘金 過去的 2016 年,開源社區異常活躍,很多個人與公司爭相開源自己的項目,讓人眼花繚亂,然而有些項目只是曇花一...
摘要:下面是一些常見的理解性問題,每一個問題盡量用圖或代碼去描述。內容全部來自,包括基本語法數組集合類泛型面向對象垃圾回收異常控制輸入輸出和內存。不斷更新,歡迎大家提出有趣味的問題和意見。 程序員經常可以通過搜索或者記憶來完成代碼,但是許多時候并不真正理解為什么那樣寫。也就是說,有一定經驗的程序員不會犯一些低級的語法錯誤,但是因為不深入理解有可能造成一些高級錯誤,比如說運行無效率,代碼難De...
摘要:對微信小程序進行全局配置,決定頁面文件的路徑窗口表現設置網絡超時時間設置多等。 微信小程序知識總結及案例集錦 微信小程序的發展會和微信公眾號一樣,在某個時間點爆發 學習路徑 微信小程序最好的教程肯定是官方的文檔啦,點擊這里直達 微信官方文檔 認真跟著文檔看一遍,相信有vue前端經驗的看下應該就能上手了,然后安裝 微信小程序開發者工具 新建一個quick start項目,了解代碼結構,...
閱讀 4675·2021-09-22 16:06
閱讀 2073·2021-09-22 15:22
閱讀 1409·2019-08-30 15:54
閱讀 2510·2019-08-30 15:44
閱讀 2341·2019-08-29 16:31
閱讀 2009·2019-08-29 16:26
閱讀 2327·2019-08-29 12:41
閱讀 730·2019-08-29 12:22